Back to Home

1 Introduction

This project consists of a comparison of execution time and memory use in Python and R for computational operations common in data science, namely generic loop and vectorized operations, matrix multiplication and inversion, and some popular computationally heavy statistical and ML algorithms.

1.1 Why R and Python

R and Python are arguably the two most popular open-source programming language used in data science. Both are considered high-level programming languages with many abstractions built-in to feature simple syntax.

Python is also widely outside for programming outside data science and ranks first in many popularity indices like the TIOBE index. Its popularity in data science may be explained by its extensive ecosystem of open-source libraries contributed by the programming community, such as NumPy, pandas, scikit-learn. These libraries provide flexible algorithms for data manipulation, analysis, and visualization. Python’s ecosystem has many other libraries beyond data science, such as for web development and natural language processing, and it has extensive documentation on GitHub. Compared to R, Python is more popular in the data science industry and in the academic machine learning community. Python libraries are typically built in C/C++ or Fortran for efficient implementations.

R is a programming language designed specifically for statistical computing and graphics. Like Python, R’s main strength is its extensive collection of open-source packages contributed by the community of users, which mainly consists of statisticians and researchers. It features built-in functions for popular statistical algorithms like linear regression as well as flexible graphical capabilities. There are also many packages for complex data manipulation like the Tidyverse collection which includes dplyr and the high-quality visualization tool ggplot2. R’s syntax is tailored for statistical analysis, using vectors as its data structure for storing and manipulating data. It includes many built-in reproducibility features like setting a seed for pseudo-random number generators. Its packages are also typically implemented in C or C++.

Other popular high-level programming languages in data science include Java and relative newcomer Julia, and common proprietary programming languages include SAS, Stata, and MATLAB.

2 Setup

This project contained three phases, which are representative of a typical data science project:

  1. Simulating data;

  2. Writing and running various algorithms and recording execution time and memory usage;

  3. Analyzing and presenting the data.

2.1 Simulation

I simulated a linear regression dataset in R with \(n=10^6\) and \(p=10\) as follows: \[\begin{align*} \beta_j &\overset{iid}{\sim} \mathrm{Unif}(0,10), \quad j=1,\dots, p \\ x_{ij} &\overset{iid}{\sim} \mathcal{N}(0,1), \quad i=1, \dots,n \\ y_i &\overset{indep.}{\sim} \mathcal{N}(100+\sum_{j=1}^{p} \beta_j x_{ij},1). \end{align*}\] The \(y\) vector was used by the loop and bootstrap algorithms, and the full \((y,X)\) dataset was used by the linear regression and Markov chain Monte Carlo algorithms.

I also generated a vector \[\begin{align*} I_i &\overset{iid}{\sim} \mathrm{Bern}(1/2), \quad i=1, \dots, n\\ f_i &= (-1)^{I_i} \end{align*}\] and the datset \((f, X)\) was used by the support vector machine algorithm.

Two \(1000\times 1000\) matrices were populated from a standard Normal distribution. These matrices were used in the matrix multiplication and inversion operations. The data \((y,X,f)\) was saved as a matrix in the file Data/data.csv, and the two matrices \(A\) and \(B\) were saved in the files Data/A.csv and Data/B.csv. The script used to generate the data is available in the repository https://github.com/leovanciu/cs32_final_project at Data/data.R, and the csv files for the matrices is available in the same repository while the data.csv file was too large to be uploaded to Github but the same data I used can be generated through the fixed seed set in the R file.

The R code used to generate the data is very straightforward following probabilistic notation as follows.

set.seed(32)
# Generate linear regression data
betas <- runif(10, -10, 10)

X <- matrix(rnorm(1e6 * 10), ncol = 10)
y <- 100 + X[,1:10] %*% betas + rnorm(1e6)
f <- sample(c(-1, 1), 1e6, replace = TRUE)
data <- cbind(y, X, f)
write.csv(data, "Data/data.csv")

# Generate matrix data
A <- matrix(runif(1e6), ncol = 1e3)
B <- matrix(runif(1e6), ncol = 1e3)
write.csv(A, "Data/A.csv")
write.csv(B, "Data/B.csv")

2.2 Algorithms

For each algorithm, I measure execution time and memory using the “benchmark” package in R and the “time” and “memory_profiler” libraries in Python. The measured time was total user time rather than processor time. The algorithms were meant to be written in a way that is as structurally similar as possible in both languages, but the implementations of base functions vary greatly between the two languages. Ultimately, this is what makes the comparison interesting since there can be great differences in execution time for similar operations, but it also means that an algorithm can be written in a poorly optimized way in a language and artificially seem much faster in the other. For instance, people often criticize the speed of loops in R and Python and my tests agree with this criticism, but these operations can almost always be made much faster by using vectorized operations.

Memory turned out difficult to measure, and I relied on external packages. R and Python implement memory management in very different ways. For instance, R distinguishes between memory stored by vectors and by everything else. It appears that memory usage was too low to be recorded in R for several operations such as loops and was recorded as 0. This is a floating point problem that I was not able to solve despite trying many approaches, including the base R implementation of memory monitoring and several packages. The interpretation of memory usage is also murky, as detailed in the Dicussion section.

The following algorithms were tested: a sum loop, a geometric mean loop and a vectorized implementation, matrix multiplication and inversion, linear regression, the bootstrap, two Markov Chain Monte Carlo (MCMC) algorithms, and a support vector machine (SVM). When possible, I implemented a simple version of these algorithms using only base functions in the language as well as a popular package/library. For instance, for linear regression I use the lm() function in R and the Scikit-Learn library in Python as well as an algorithm using only matrix multiplication and inversion. Of course, these two matrix operations are themselves algorithms with many different possible implementations. I mainly use R in my everyday life so I am likely more familiar with the most optimized packages in R rather than Python.

I varied the sample size when running the algorithms by selecting a subset of these datasets. I varied the sample size from \(n=10^2\) to \(n=10^6\) by magnitude of 10 for the loop and linear regression algorithms. I varied the size of the matrices from \(n=10\) to \(n=10^3\) by magnitude of \(10^{1/2}\). I kept the sample size of the bootstrap and MCMC algorithms fixed at \(n=10^3\) and varied the sampling/resampling size (see the precise definition of the algorithms in the Results section) from \(B=10\) to \(10^5\) by magnitude of 10. For the SVM algorithm, I varied the sample size from \(n=10\) to \(n=10^3\) by magnitude of \(10^{1/2}\). The sample sizes were chosen to represent the shift from the typical size of a statistical analysis around \(n=100\) to the big data size of \(n=10^6\) which approaches the limits of my machine for many algorithms. The sample size was varied at lower rates for some algorithms because execution time increased too rapidly for the bootstrap, Markov chain Monte Carlo algorithms, and the support vector machine.

I ran scripts in R and Python on these simulated datasets for 10 iterations and for the varying sample sizes mentioned above, and I recorded the median execution time and memory use in CSV files in Results/results_R.csv and Results/results_python.csv. The scripts used to run the algorithms are available in the GitHub repository at Scripts/algorithms.py and Scripts/algorithms.R.

2.3 Analysis

I used R Markdown to generate this HTML webpage and used the plotly package to generate interactive plots of execution time and memory use for each algorithm. I also discuss theoretical Big-O complexity and fit an empirical curve to the plots based on the most simple version of these algorithms.

2.4 Reproducibility

Details on reproducing the entire project can be found at https://github.com/leovanciu/cs32_final_project. The code for this Rmarkdown file can be downloaded by clicking on the code button at the top of this webpage or at https://github.com/leovanciu/leovanciu.github.io/blob/master/cs32-project.Rmd. All the scripts for simulating the data and running the scripts are available in the GitHub repository above, and all the code used to generate my website which was forked from mmistakes/minimal-mistakes is available in the Github repository https://github.com/leovanciu/leovanciu.github.io.

I am using R version 4.3.1 with RStudio version 2023.09.0+463 and Python version 3.12.3 with Visual Studio code version 1.88.1. My computer is a MacBook Pro (13-inch, M1, 2020) with 8 GB of memory and running on macOS Monterey Version 12.2.1.

3 Results

3.1 Data

A histogram of the simulated data for \(n=10^5\) is plotted below. It has mean 100 and standard deviation 17. By construction, it is Normally distributed.

library(plotly)
data <- read.csv("Data/data.csv")
hist <- plot_ly(x = data[1:1e5,2], type = "histogram") %>%
  layout(title = "Simulated Data",
         yaxis = list(title = "Frequency"),
          xaxis = list(title = "y"))
hist

3.2 Loop operations

For loops are ubiquitous programming structures in data science, and they are somewhat infamous in both R and Python, as they are believed to be much slower than in low-level languages like C.

3.2.1 Loop sums

Here is pseudo code of a simple foreach loop to compute a sum.

Function loop_sum(n)
    sum <- 0
    For i from 1 to n
        sum <- sum + i
    End For
    Return sum
End Function

In terms of computational complexity, the initialization step is \(O(1)\), a linear operation like a sum is also \(O(1)\), and by definition the loop is \(O(n)\) which is the overall complexity of the algorithm.

The following code shows the implementation in Python.

def loop_sum(n):
    sum = 0
    for i in range(n):
        sum += 1
    return sum

The following code shows the implementation in R.

loop_sum <- function(n) {
  sum <- 0
  for (i in 1:n) {
    sum <- sum + 1
  }
  return(sum)
}

Note that the syntax for writing functions is slightly different in R and Python. Both languages use foreach loops written similarly. R supports both <- and = as assignement operators. Historically, comes from the S language which also uses the <- assignment operator.

I include below the code used to generate interactive plots of execution time and memory usage with the plotly library in R. Generating this plot required quite a bit of data manipulation, which was easily done in base R, and more complex operations can be done with simple syntax with the dplyr package, which I used when storing the results from the benchmarks. All other plots were generated with similar code hidden for readability.

library(plotly)
# Read data
R <- read.csv("/Users/ancavanciupopescu/Desktop/Classes/CS 32/Final project/Results/Results_R.csv")
python <- read.csv("/Users/ancavanciupopescu/Desktop/Classes/CS 32/Final project/Results/Results_python.csv")
merged_data <- merge(R, python, by=c("Algorithm", "n"), suffixes = c("R", "python"))
algorithm_names <- c('loop_sum','loop_geom_mean', 'vectorized_geom_mean', 'matrix_multiplication', 
                 'matrix_inversion', 'linear_regression_package', 'linear_regression_base', 
                 'bootstrap_package', 'bootstrap_base', 'svm_package', 'svm_base', 
                 'Metropolis_Hastings', 'MCMC_stan')

# Compute Big-O complexity
algos <- c("loop_sum") # We only plot one algorithm here but in other plots I have multiple algorithms
n_values <- seq(2, 6, by = 0.1)
O_n <- 10^n_values/10^4
O_n_python_1 <- O_n*python[python$Algorithm == algos[1],]$Time[3] # The pre-factors are standardized on the empirical time with n=10^4
O_n_R_1 <- O_n*R[R$Algorithm == algos[1],]$Time[3]

# Time plot
plot_ly() %>%
  add_trace(data = R[R$Algorithm == algos[1],], x = ~log(n, 10), y = ~Time, 
            type = 'scatter', mode = 'lines+markers', name = 'R',
            line = list(color = "blue", dash = 'solid'),
            marker = list(color = "blue", symbol = 'circle')) %>%
  add_trace(data = python[python$Algorithm == algos[1],], x = ~log(n, 10), y = ~Time, 
            type = 'scatter', mode = 'lines+markers', name = 'loop_sum Python',
            line = list(color = "red", dash = 'solid'),
            marker = list(color = "red", symbol = 'dot')) %>%
  add_trace(x = n_values, y = O_n_python_1, type = 'scatter', mode = 'lines',
            name = 'O(n)', line = list(color = 'black', dash = 'dash')) %>%
  add_trace(x = n_values, y = O_n_R_1, type = 'scatter', mode = 'lines',
            name = 'O(n)', line = list(color = 'black', dash = 'dash'), showlegend = FALSE) %>%
  layout(title = "Execution Time",
         xaxis = list(title = "log(n)", tickmode = "array", tickvals = 0:6, ticktext = as.character(0:6)),
         yaxis = list(title = "Time (s)"),
         legend = list(title = "Legend", orientation = "h", x = 0.3, y = -0.2))

We see that loops in R run two to three times faster than in Pyton. We see that \(O(n)\) fits the data perfectly. It seems that memory usage was too low to be recorded in R and there is no clear trend on the memory usage in Python, although it Python does use a strangely large amount of memory for such a simple for loop algorithm. This is a win for execution time for R, but it is well-known that loops are slow in both R and Python due to the many abstractions made by the programming languages. It is often suggested to vectorize operations, which can speed up loops considerably.

3.2.2 Vectorized operations

Vectorized operations are operations applied to every entry of an array. They are still loops, but they are typically compiled directly in C which is much faster than executing a loop in R or Python. The computational complexity is theoretically should still be the same as a loop since the same operations are performed, but the pre-factor is reduced. In addition, the operations can be optimized for the CPU to perform multiple operations on the data. Hence, due to more efficient memory allocation it is possible for the order of execution time to be reduced as well. In the following plots, I compare the computation of the geometric mean computed as \[\exp\left(\frac{1}{n} \sum_{i=1}^n \log y_i\right)\] through a loop and with a vectorized operation.

R has a distinct vector object type, while in Python they are simply one-dimensional arrays. This is implemented in Python through libraries such as numpy and natively in R although there are also several packages implementing efficient vectorized operations. Here is the code in Python:

def vectorized_geom_mean(data):
    return np.exp(np.sum(np.log(data)) / len(data))

and in R:

vectorized_geom_mean <- function(data) {
  return(exp(mean(log(data))))
}

Here are plots showing execution time and memory for plots and vectorized operations in R and Python.

We see that loops in Python and R are very slow compared to the vectorized operation. Although the pre-factor is much smaller for vectorized operations, the computational complexity is still \(O(n)\), as can be seen by fitting a linear curve to the plot of vectorized operations below.

3.3 Matrix operations

Matrix operations, namely addition, subtraction, multiplication, inversion, and dot products are central to statistics particularly for multivariate data. For instance, linear regression can be framed entirely as an algorithm involving only matrix multiplication and inversion. Here, I test the implementation of matrix multiplication and inversion in Python and R. This is a one-liner in both languages, but the actual algorithms for matrix multiplication and inversion are actually quite computationally costly.

For matrix multiplication, the mathematical definition gives the following algorithm.

Function mutiply_matrices(A, B)
    // Dimensions
    m <- number of rows in A
    n <- number of columns in A
    p <- number of columns in B
    C <- Initialize with zeros
    
    For i from 1 to m
        For j from 1 to p
            sum <- 0
            For k from 1 to n
                sum <- sum + A[i][k] * B[k][j]
            EndFor
            C[i][j] <- sum
        EndFor
    EndFor
    
    Return C
End Function

For each pair of \(m\) row from \(A\) and \(p\) column from \(B\), \(n\) multiplications are performed through three nested loops. For each of the \(n\) multiplications, there is a sum which has complexity \(O(1)\). This gives computational complexity \(\mathcal{O}(mnp)\) and in the case where \(m=n=p\) this is \(O(n^3)\). However, Python and R likely have a more efficient version through some clever algorithm.

Here is basic pseudocode for the Gauss-Jordan algorithm for matrix inversion.

Function invert_matrix(A)
    // Dimensions
    n <- number of rows in A
    B <- Initialize matrix B as an identity matrix
    
    // Perform Gaussian elimination
    For i from 1 to n
        divisor <- A[i][i]
        For j from 1 to n
            A[i][j] <- A[i][j] / divisor
            B[i][j] <- B[i][j] / divisor
        EndFor
        
        // Set other elements in column i of A to 0
        For k from 1 to n
            If k != i
                factor <- A[k][i]
                For j from 1 to n
                    A[k][j] <- A[k][j] - factor * A[i][j]
                    B[k][j] <- B[k][j] - factor * B[i][j]
                EndFor
            EndIf
        EndFor
    EndFor
    
    Return B
End Function

Likewise to matrix multiplication, there are three nested loops which gives complexity \(\mathcal{O}(n^3)\).

In Python this is simply implemented as follows.

def multiply_matrices(A, B):
    return A @ B

def invert_matrix(A):
    return np.linalg.inv(A)

Likewise in R this is simply implemented as follows. Sidenote: I really do not like the syntax %*% for matrix multiplication in R.

multiply_matrices <- function(A, B) {
  return(A %*% B)
}

invert_matrix <- function(A) {
  return(solve(A))
}

Interestingly, the implementation of matrix multiplication and inversion in Python seems to scale linearly or even better with \(n\), but it blows up to order \(O(n^3)\) for R for larger matrices. For smaller matrices of size less than \(100 \times 100\), R seems to be faster while Python is much faster for large \(n=1000\). An important note is that memory also seems to increase linearly for larger matrices in Python which may also explain why the execution time is much better than the theoretical complexity.

3.4 Linear regression

The ordinary least squares estimate of linear regression \(\hat{\beta}\) for \(\beta\) is the vector that solves the contrainst \[\arg \min_\beta (y - X \beta^T)^2.\] If the data is Normally distributed as in the simulation, then it is well-known that \(\hat{\beta}\) is given by \[\hat{\beta} = (X^T X)^{-1} X^T y.\] Therefore, OLS consists simply of matrix multiplication and inversion. In pseudocode, it can be implemented in the following manner.

Function linear_regression(X, y)
    // Dimensions
    n <- length of data y
    p <- number of columns in X
    X_b <- Append column 1 to X // Include an intercept
    
    // Compute coefficients
    beta_hat <- (X_b.T @ X_b)^-1 @ (X_b.T @ y) 
    
    Return beta_hat
End Function

The matrix augmentation operation is \(O(n)\) since we are appending \(n\) rows. The matrix multiplication and inversion are the dominating steps of this algorithm. As explained in the previous section on matrices, the matrix multiplication X_b.T @ X_b step is \(O(n \times (p+1)^2)\) while the matrix inversion is \(O((p+1)^3)\), although these matrix operations are likely implemented more efficiently in Python and R. The last step of multiplying the matrix (X_b.T @ X_b)^-1 with the vector (X_b.T @ y) is \(O((p+1)^2)\). Therefore, the overall complexity is \(O(p^3)\) or \(O(n \times (p+1)^2)\) depending on whether \(p<<n\). We can never have \(p>n\) in linear regression since the coefficients would become unidentifiable. In this case since we keep \(p=10\) and we increase \(n\) to \(n=10^6\), the dominating step is the first matrix multiplication step which is \(O(n)\). This setup is typical of classical regression where a relatively small number of covariates is used, but in a high-dimensional setting we could have large \(p\) which would make the matrix inversion step more costly.

In Python this can be implemented like the algorithm above or using the Scikit-Learn package:

# Linear regression from base
def lin_reg_base(X, y):
    X_b = np.hstack([np.ones((X.shape[0], 1)), X])
    beta_hat = np.linalg.inv(X_b.T @ X_b) @ (X_b.T @ y)
    return beta_hat
  
# sklearn-Learn linear regression
def lin_reg_sklearn(X, y):
    model = LinearRegression()
    model.fit(X, y)
    beta_hat = np.hstack([model.intercept_, model.coef_])
    return beta_hat

and in R this is also implemented in Base R using the lm() function:

# Linear regression from base
lin_reg_base <- function(X, y) {
  Xb <- cbind(1, X)
  betas <- solve(t(Xb) %*% Xb) %*% (t(Xb) %*% y)
  return(betas)
}

# Linear regression using lm()
lin_reg_package <- function(X, y) {
  model <- lm(y ~ X + 0)
  return(coef(model))
}

Linear regression seems to scale linearly for execution time in R, but execution time seems constant and even perplexingly decreases for large \(n\) in Python. The implementation from scratch in both Python and R was faster than the implementation from sklearn in Python and from the base function in R, and R was faster for data sets less than \(n=10^5\) while Python scaled better for large \(n=10^6\). The execution time of linear regression from sklearn blows up at \(10^6\) and is considerably slower than all other implementations. This makes sense since scikit-learn and lm() contain additional steps than my simple code, such as translating the formula syntax into matrix calculations and many other options for more complicated versions of linear regression, although I am not sure why it scales so poorly for large \(n\). Interestingly, memory scaled roughly linearly and all algorithms in both R and Python had similar memory curves, which may explain why execution time did not seem to increase in the basic implemention in Python.

3.5 Bootstrap

The bootstrap algorithm is popular in statistics because it allows for the estimation of the properties of general estimators under minimal assumptions even if they are not analytically tractable. The idea of the bootstrap is to resample from the data to create new synthethic datasets and compute a new estimator for each dataset, from which the sampling distribution of the estimator can be estimated. The bootstrap was influential in moving statistics away from mathematics and towards computation since one does not need to know anything about the distribution of the estimator to perform the algorithm. The major assumptions of the standard bootstrap are that the data are independent and identically distributed and that the empirical cumulative density function (CDF) is close to the true CDF of the data. The latter is asymptotically justified but empirically untestable.

Here is a pseudocode for the bootstrap algorithm.

Function bootstrap(data, statistic, B, alpha)
    n <- length of data
    idx <- sample B sets of indices with replacement 
    samples <- extract data elements based on idx
    stat <- sort(statistic(samples))
    lower_CI <- extract (100*(1-alpha)/2)th sample
    upper_CI <- extract (100*(1-(1-alpha)/2)th sample
    Return lower_CI, upper_CI
End Function

The idx resampling step is \(O(n \times B)\) and extracting the samples is also \(O(n \times B)\). For a linear statistic like a sample mean, the statistic step is also \(O(n \times B)\). The sorting step depends on the algorithm used, but the one used in Python is Timsort which has \(O(B \log (B))\). After that, computing the two sample quantiles on the sorted data is \(O(1)\). However, a potentially more efficient way of doing this step is using sample quantiles since we are only interested in two sample quantiles which may avoid sorting the entire dataset. Therefore, since we increase \(B\) and keep \(n=10^3\) fixed, the dominating step is likely the resampling/extracting/statistic step, which has complexity \(O(B)\), although if we use a naive sorting algorithm like bubble sort this may become costly. There are other more complicated implementations of the bootstrap that do not use sample quantiles for the confidence interval. A popular approach is to use a bootstrapped pivot within each iteration of the algorithm, thereby nesting an additional loop in the algorithm which increases the complexity to \(O(B^2)\).

In Python the standard bootstrap can be implemented using the algorithm above with vectorized operations for efficiency, or using the scipy.stats library as follows.

# Bootstrap from base
def bootstrap_base(data, statistic, B):
    n = len(data)
    idx = np.random.randint(0, n, (B, n))
    samples = data[idx]
    stat = statistic(samples)
    confidence_interval = np.quantile(stat, [0.025, 0.975])
    return confidence_interval
  
# scipy.stats bootstrap
def bootstrap_scipy(data, statistic, B, confidence_level=0.95):
    res = stats.bootstrap((data,), statistic, n_resamples=B, confidence_level=confidence_level, method='percentile')
    return res.confidence_interval
  
def sample_mean(data):
    return sum(data)/len(data)

In R this can also be implemented using a vectorized version or using the boot package.

# Bootstrap from base
bootstrap_base <- function(data, statistic, B) {
  n <- length(data)
  results <- replicate(B, statistic(sample(data, size = length(data), replace = TRUE)))
  ci <- quantile(results, probs = c(0.025, 0.975))
  return(ci)
}
mean_function <- function(data) {
  mean(data)
}

# Bootstrap from boot
bootstrap_package <- function(data, statistic, R) {
  result <- boot(data, statistic = statistic, R = R)
  ci <- boot.ci(result, type = "perc")$percent[4:5]
  return(ci)
}

mean_boot <- function(data, indices) {
  mean(data[indices])
}

In the plots above, we see that the execution time for both the package and base implementations of the bootstrap in Python blow up for large \(B=10^5\). Since the plots are interactive, I encourage you to remove the Python data points by cliking on them in the legend. Then we can see that the implementations in R scale linearly as expected from the theoretical complexity. We also see that memory increases extremely fast in both R and Python, reaching 4GB for \(B=10^5\) in Python and 2GB in R. Running \(10^5\) iterations is likely overkill in practice, particularly for a sample mean which has rapid convergence by the law of large numbers. In general, the Monte Carlo error for a simple estimator often becomes much smaller then the sampling variance above \(10^3\) or \(10^4\) iterations.

3.6 MCMC

Markov chain Monte Carlo algorithms (MCMC) are central methods to Bayesian statistics since they allow us to estimate the posterior distribution of parameters with general priors and likelihoods. Bayes theorem shows how to obtain a posterior distribution by multiplying the prior with the likelihood and dividing by the marginal distribution which involves taking an integral. In a few cases like the Normal distribution, the posterior distribution is of the same family as the prior and a closed form is available. However, for arbitrary priors and likelihoods this integral is typically very difficult or intractable, and numerical methods for computing integrals on a grid suffer from the curse of dimensionality. MCMC algorithms such as the Metropolis-Hastings algorithm approximates the posterior distribution by iteratively generating samples from a proposal distribution and computing an acceptance ratio that allows the sample to approach the posterior distribution. The acceptance ratio depends only on the current and previous sample, and it avoids having to compute the normalization constant since it cancels out in the ratio. A simple proposal function uses the Gaussian distribution at the current point, while more other MCMC algorithms like Hamiltonian Monte Carlo use more complicated proposal distributions by leveraging Hamiltonian dynamics. MCMC algorithms are well justified theoretically, and unlike the bootstrap it is possible to assess the convergence of the algorithm by running multiple chains and examining whether the chains converge to the same distribution, although I do not consider this here.

Here is a simple pseudocode implementation of the Metropolis Hastings algorithm for the same linear regression setting as before. Linear regression is actually conjugate with Normal distributions which I use here for simplicity, so Metropolis-Hastings or even any approximation is not strictly necessary in this setting.

Function metropolis_hastings(X, y, B, beta_0, proposal_sd, sigma)
    // Include an intercept
    X_b <- Append column 1 to X
    
    // Initialize parameters
    current_beta <- beta_0
    samples <- current_beta
    Xb <- X_b dot current_beta
    current_likelihood <- sum(log(N(y|Xb,sigma))
    current_prior <- sum(log(N(current_beta|mu0,sigma0)))
    
    // Sampling
    For i from 1 to B
        // Proposal
        proposed_beta <- sample N(current_beta,proposal_sd)
        Xb_proposed <- X_b dot proposed_beta
        proposed_likelihood <- sum(log(N(y|Xb_proposed,sigma))
        proposed_prior <- sum(log(N(proposed_beta|mu0,sigma0)))
        
        // Accept/reject new beta
        p_accept <- exp(proposed_likelihood + proposed_prior - current_likelihood - current_prior)
        U <- sample Unif(0,1)
        If U < p_accept
            current_beta <- proposed_beta
            current_likelihood <- proposed_likelihood
            current_prior <- proposed_prior
        
        Append current_beta to samples
    
    Return samples
End Function

Augmenting the matrix is \(O(n)\), the matrix dot products from linear regression is \(O(n \times p)\), computing the log-likelihood is \(O(n)\) and computing the prior is \(O(p)\). Therefore, running the loop for B iterations has complexity \(O(n \times p \times B)\), or keeping all else fixed it is \(O(B)\). This seems great since we seem to have avoided curse of dimensionality, but it hides the important fact that this algorithm must be run until it converges to the posterior distribution. This is actually highly dependent on dimension because the random walk will need to run much longer in high dimensions to explore the entire space of the posterior distribution. Some algorithms like Hamiltonian Monte Carlo implemented in Stan in both R and Python help address this issue though more complicated proposal functions that reduce the autocorrelation between proposals.

The Metropolis-Hastings algorithm can be implemented in Python as follows.

# Metropolis_hastings from scratch
def metropolis_hastings(X, y, num_samples, beta_0=np.zeros(11), proposal_sd=1, sigma=1):
    X = np.hstack([np.ones((X.shape[0], 1)), X])
    current_beta = beta_0
    samples = [current_beta]
    
    Xb = X.dot(current_beta)
    current_likelihood = np.sum(stats.norm.logpdf(y, Xb, sigma))
    current_prior = np.sum(stats.norm.logpdf(current_beta, 0, 10))

    for i in range(num_samples):
        proposed_beta = np.random.normal(current_beta, proposal_sd)
        Xb_proposed = X.dot(proposed_beta)
        proposed_likelihood = np.sum(stats.norm.logpdf(y, Xb_proposed, sigma))
        proposed_prior = np.sum(stats.norm.logpdf(proposed_beta, 0, 10))
        
        p_accept = np.exp((proposed_likelihood + proposed_prior) - (current_likelihood + current_prior))
        
        if np.random.rand() < p_accept:
            current_beta = proposed_beta
            current_likelihood = proposed_likelihood
            current_prior = proposed_prior
        
        samples.append(current_beta)
    
    return np.array(samples)

The Metropolis-Hastings algorithm can be implemented in a similar form in R as follows.

# Metropolis_hastings for linear regression from base R
metropolis_hastings <- function(X, y, num_samples, beta_0 = rep(0,11), proposal_sd=1, sigma=1) {
  X <- cbind(1, X)
  current_beta <- beta_0
  samples <- list(current_beta)
    
  Xb <- X %*% current_beta
  current_likelihood <- sum(dnorm(y, Xb, sigma, log = TRUE))
  current_prior <- sum(dnorm(current_beta, 0, 10, log = TRUE))
  
  for (i in 1:num_samples) {
    proposed_beta <- mvrnorm(1, current_beta, diag(rep(proposal_sd, length(beta_0))))
    Xb_proposed <- X %*% proposed_beta
    proposed_likelihood <- sum(dnorm(y, Xb_proposed, sigma, log = TRUE))
    proposed_prior <- sum(dnorm(proposed_beta, 0, 10, log = TRUE))
    
    p_accept <- exp((proposed_likelihood + proposed_prior) - (current_likelihood + current_prior))
    
    if (runif(1) < p_accept) {
      current_beta <- proposed_beta
      current_likelihood <- proposed_likelihood
      current_prior <- proposed_prior
    }
    
    samples[[i + 1]] <- current_beta
  }
  
  
  return(do.call(rbind, samples))
}

Both R and Python also support interfaces for the Stan programming language using cmdstanpy in Python and Rstan in R. Stan performs efficient Hamiltonian Monte Carlo with a No-U-Turn sampler. The notation in Stan is probabilistic which is convenient for statistical model building, and the computations are compiled efficiently in C++. Here is an example of a non-conjugate implementation of linear regression with a Cauchy prior on the standard deviation.

data {
int N; // Sample size
int K; // Number of predictors
matrix[N, K] X; // Covariates
vector[N] y; // Outcome
}
parameters {
vector[K] beta;
real sigma;
}

model {
// Priors
beta ~ normal(0, 10);
sigma ~ cauchy(0, 5);

// Likelihood
y ~ normal(X * beta , sigma);
}

Both Python and R’s execution times for Metropolis-Hastings seem to scale linearly with time as expected from the theoretical complexity, and R generally seems to be faster than Python particularly for a large number iterations at \(B=10^5\). In this simple case of linear regression, running \(10^5\) iterations is definitely uncessary and convergence was achieved way before but this may be needed for very complex models. Stan’s execution time is very similar in both interfaces and it seems to scale even better than linearly for large number of iterations. Since Stan has much lower autocorrelation in the chains than Metroplis-Hastings, Stan actually has much higher effective sample size which makes it performance quite impressive, and \(10^5\) iterations is typically more than necessary in Stan. We also see that memory usage blows up in R with 3GB of memory used for \(10^5\) iterations, but it stays surprisingly low below 100MiB in Python.

3.7 SVM

I will not say much about support vector machines (SVM) because I am not as familiar with this algorithm compared to statistical ones. Nonetheless, I thought it would be interesting to include a machine learning algorithm since this is often thought to be a major strength of Python through its large ecosystem of libraries. My understanding of the algorithm using a simple form of gradient descent is as follows.

Function svm(X, y, epochs, learning_rate, C)
    // Initialize weight and bias parameters
    w <- vector 0
    b <- 0
    
    For each epoch from 1 to epochs
        For each sample i from 1 to length of y
            Decision_value <- X[i] dot (w + b)
            
            // Check if data is on the correct side of the margin
            If y[i] * decision_value < 1 then
                // Update w and b for incorrectly classified samples
                w <- w + learning_rate * (y[i] * X[i] - 2 * (1/C) * w)
                b <- b + learning_rate * y[i]
            Else
                // Update regularization for correctly classified samples
                w <- w - learning_rate * (2 * (1/C) * w)
            
    Return w, b
End Function

The initializations are \(O(p)\) and \(O(1)\), the outer loop runs for epochs iterations and the inner loop runs for \(n\) interations, the product X[i] dot C is \(O(p)\), so the overall complexity is \(O(\text{epochs} \times n \times p)\). In this case, I am keeping epochs and p fixed and am increasing the sample size, so the complexity is \(O(n)\).

This is implemented in Python following the pseudocode and using the sklearn library as follows.

# SVM from base
def svm_base(X, y, epochs=100, learning_rate=0.01, C=1.0):
    w = np.zeros(X.shape[1])
    b = 0
    for epoch in range(epochs):
        for i in range(len(y)):
            if y[i] * (np.dot(X[i], w) + b) < 1:
                w += learning_rate * ((y[i] * X[i]) + (-2 * (1/C) * w))
                b += learning_rate * y[i]
            else:
                w -= learning_rate * (2 * (1/C) * w)
    return w, b

# sklearn-Learn SVM
def svm_sklearn(X, y):
    clf = SVC(kernel='linear', C=1.0)
    clf.fit(X, y)
    return clf.coef_[0], clf.intercept_[0]

Likewise, this is implemented in R following a similar form and using a package called e1071.

# Simple linear SVM from base
svm_base <- function(X, y, epochs = 100, learning_rate = 0.01, lambda = 0.01) {
  n_samples <- nrow(X)
  n_features <- ncol(X)
  weights <- matrix(0, nrow = n_features, ncol = 1)
  intercept <- 0
  
  for (epoch in 1:epochs) {
    for (i in 1:n_samples) {
      if (y[i] * (crossprod(weights, X[i, ]) + intercept) < 1) {
        weights <- weights + learning_rate * ((y[i] * matrix(X[i, ], ncol = 1)) - (2 * lambda * weights))
        intercept <- intercept + learning_rate * y[i]
      } else {
        weights <- weights - learning_rate * (2 * lambda * weights)
      }
    }
  }
  list(coefficients = weights, intercept = intercept)
}

# SVM using e1071 package
svm_e1071 <- function(X, y) {
  model <- svm(x = X, y = as.factor(y), kernel = "linear")
  list(coefficients = t(as.vector(model$coefs) %*% model$SV), intercept = -model$rho)
}

Both the R and Python implementations from scratch seem to scale roughly linearly with sample size and R is generally faster than Python, but the package implementations of SVM are considerably faster. Memory usage is again strangely very low in R and there is no discernable trend.

4 Discussion

4.1 Execution time

R generally had better execution times for smaller to medium-sized datasets across all types of operations I tested. More specifically, R had faster loops for all sample sizes, faster vectorized operations for small-medium sample sizes, and faster matrix operations for small-medium sample sizes. Consistent with these results, linear regression which is just implemented as matrix operations was faster for small-medium sample sizes, and the bootstrap, MCMC, and SVM which are loop algorithms were faster overall in R. This may be due to R’s efficient handling of vectorized operations and its optimization for operations commonly used in statistics and data manipulation.

However, Python exhibited better performance with large datasets, particularly in vectorized operations and matrix manipulations. Likewise, linear regression was faster in Python for large sample sizes. This is possibly due to its efficient libraries like NumPy, which are designed to handle large scale data efficiently.

4.2 Memory usage

In most cases, I do not think that memory usage was well-measured in R, so I will not make overall statements about memory managemen. However, the bootstrap and MCMC were costly for memory in both languages. In addition, memory management in Python seemed to be for the most part agnostic to the type of operations executed, using similar memory for a simple sum loop and for the support vector machine algorithm. In addition, Python did seem to request much more of my computer’s memory than R, and I had to leave it running overnight to avoid running other operations simultaneously on my computer, but I do not think this is well-quantified. In addition, both languages are garbage-collected language, meaning that they automatically release memory when they check that the objects are not used, but memory at any given time is not necessarily representative of computational complexity.

Even if the memory usage of an operation was well-measured in R and Python, it is not clear if it is desirable to have low or high memory usage, since low memory usage could be due to an efficient implementation or rather be due to poor memory attribution which ends up slowing down the program. Hence, there is a tradeoff between execution time and memory storage.

4.3 Other considerations

In terms of code writing, I personally prefer R for most data science projects because I can very quickly analzye data in a couple lines without thinking much about the syntax. Also, I find the syntax of dplyr very clear for data manipulation, and I think that the high quality visualization of ggplot2 is clearly unmatched by matplotlib or any other library in python. However, if I have to implement a more complex operation, I often find R’s syntax frustrating since I constantly have to look up obscure notations, and the execution is certainly much slower than a low-level language like C. On the other hand, Python is much better-supported for machine learning and for general purpose programming.

In this project, a challenge for me was to store the simulated manner that can be easily read in both Python and R. If I was using just R I would use a .rds file containing all the data in an object with complex structure that can be easily imported. However, to be read in Python I had to save the data in several .csv files which were read with an awkward structure in Python and I had to include a worrisome errors=‘coerce’ line which solved the problem that my file included both strings and numerics. This is mainly my fault because I do not know how to pogram in Python well, but R makes it (dangerously) easy to use different objects without thinking about structure too much. I find this to be a great advantage. For the purpose of this project, I did not really care much about importing the data smoothly since I was focused on making the algorithms comparable. An interesting aspect that I did not test was the speed of reading data, which I clearly don’t know enough about to test fairly, but it can be a major consideration for large datasets. Another thing I did not consider was parallel computing, which can be implemented in both R and Python and can significantly speed up computations. There are also many other programming languages than Python and R that can be used in data science, such as Julia or on the other end of abstraction, C/C++.

4.4 Interactive visualization

I used plotly to make the interactive plots in this page, and it is the first time I used interactive visualization. I did not really like this experience because I think the plots mostly end up distracting the user without conveying much more information. I find exploratory data visualization much easier and beautiful in ggplot2 or even base R. However, it also seems more interesting and eye-catching than a black-and-white plot you would read in a paper, particularly for blog post format like this.

I also chose to plot the data on a log scale, which is difficult to interpret. For instance, we see that linear functions become very distorted. However, I thought this choice was reasonable to highlight the different needs of data science from small samples, which is my main area of interest, to big data. As a statistician, I also would have wished to convey uncertainty in these estimates. I used 10 iterations of each algorithm and computed the median, but it may have been interesting to find a way to include uncertainty on the plots, although it would have made the plots more cluttered than they already are.

This was also my first time creating a website, which I forked from mmistakes/minimal-mistakes. I liked this format of using markdown notebook, which allowed to easily include text, code, and plots from different programming languages in an aesthetically pleasing format. However, the easiness of writing in a markdown file as opposed to HTML and CSS files comes at the cost of customization.

4.5 Is R or Python better?

Clearly, computational performance is highly dependent on the situation, including the data dimension, the algorithm of interest, the easiness of optimizing code, and the machine used to compute it. Therefore, I cannot even answer the question of whether R or Python is faster for my uses and my computer, let alone for anyone else. However, in most of my everyday data science operations, my sample sizes are below \(10^5\), and this simulation shows me that in that context R keeps up with Python and, to my surprise, is often better. In some cases, such as very large sample sizes or machine learning algorithms with well-implemented libraries, Python may be significantly faster. Overall, it does seem that R was much faster for me, which is particularly clear through the fact that the entire script was run in 9 minutes in R and in 126 minutes in Python.

LS0tCnRpdGxlOiAiQ29tcGFyaXNvbiBvZiBDb21wdXRhdGlvbmFsIFBlcmZvcm1hbmNlIG9mIFIgYW5kIFB5dGhvbiBmb3IgRGF0YSBTY2llbmNlIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRoZW1lOiBsdW1lbgotLS0KYGBge3IsIHNldHVwLCBlY2hvPUZBTFNFfQpsaWJyYXJ5KHJldGljdWxhdGUpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIi9Vc2Vycy9hbmNhdmFuY2l1cG9wZXNjdS9EZXNrdG9wL0NsYXNzZXMvQ1MgMzIvY3MzMl9maW5hbF9wcm9qZWN0LyIpIApgYGAKPGEgaHJlZj0naHR0cHM6Ly9sZW92YW5jaXUuZ2l0aHViLmlvLycgY2xhc3M9J2J0biBidG4tcHJpbWFyeSc+QmFjayB0byBIb21lPC9hPgoKIyBJbnRyb2R1Y3Rpb24KClRoaXMgcHJvamVjdCBjb25zaXN0cyBvZiBhIGNvbXBhcmlzb24gb2YgZXhlY3V0aW9uIHRpbWUgYW5kIG1lbW9yeSB1c2UgaW4gUHl0aG9uIGFuZCBSIGZvciBjb21wdXRhdGlvbmFsIG9wZXJhdGlvbnMgY29tbW9uIGluIGRhdGEgc2NpZW5jZSwgbmFtZWx5IGdlbmVyaWMgbG9vcCBhbmQgdmVjdG9yaXplZCBvcGVyYXRpb25zLCBtYXRyaXggbXVsdGlwbGljYXRpb24gYW5kIGludmVyc2lvbiwgYW5kIHNvbWUgcG9wdWxhciBjb21wdXRhdGlvbmFsbHkgaGVhdnkgc3RhdGlzdGljYWwgYW5kIE1MIGFsZ29yaXRobXMuCgoKIyMgV2h5IFIgYW5kIFB5dGhvbgoKUiBhbmQgUHl0aG9uIGFyZSBhcmd1YWJseSB0aGUgdHdvIG1vc3QgcG9wdWxhciBvcGVuLXNvdXJjZSBwcm9ncmFtbWluZyBsYW5ndWFnZSB1c2VkIGluIGRhdGEgc2NpZW5jZS4gQm90aCBhcmUgY29uc2lkZXJlZCBoaWdoLWxldmVsIHByb2dyYW1taW5nIGxhbmd1YWdlcyB3aXRoIG1hbnkgYWJzdHJhY3Rpb25zIGJ1aWx0LWluIHRvIGZlYXR1cmUgc2ltcGxlIHN5bnRheC4KClB5dGhvbiBpcyBhbHNvIHdpZGVseSBvdXRzaWRlIGZvciBwcm9ncmFtbWluZyBvdXRzaWRlIGRhdGEgc2NpZW5jZSBhbmQgcmFua3MgZmlyc3QgaW4gbWFueSBwb3B1bGFyaXR5IGluZGljZXMgbGlrZSB0aGUgVElPQkUgaW5kZXguIEl0cyBwb3B1bGFyaXR5IGluIGRhdGEgc2NpZW5jZSBtYXkgYmUgZXhwbGFpbmVkIGJ5IGl0cyBleHRlbnNpdmUgZWNvc3lzdGVtIG9mIG9wZW4tc291cmNlIGxpYnJhcmllcyBjb250cmlidXRlZCBieSB0aGUgcHJvZ3JhbW1pbmcgY29tbXVuaXR5LCBzdWNoIGFzIE51bVB5LCBwYW5kYXMsIHNjaWtpdC1sZWFybi4gVGhlc2UgbGlicmFyaWVzIHByb3ZpZGUgZmxleGlibGUgYWxnb3JpdGhtcyBmb3IgZGF0YSBtYW5pcHVsYXRpb24sIGFuYWx5c2lzLCBhbmQgdmlzdWFsaXphdGlvbi4gUHl0aG9uJ3MgZWNvc3lzdGVtIGhhcyBtYW55IG90aGVyIGxpYnJhcmllcyBiZXlvbmQgZGF0YSBzY2llbmNlLCBzdWNoIGFzIGZvciB3ZWIgZGV2ZWxvcG1lbnQgYW5kIG5hdHVyYWwgbGFuZ3VhZ2UgcHJvY2Vzc2luZywgYW5kIGl0IGhhcyBleHRlbnNpdmUgZG9jdW1lbnRhdGlvbiBvbiBHaXRIdWIuIENvbXBhcmVkIHRvIFIsIFB5dGhvbiBpcyBtb3JlIHBvcHVsYXIgaW4gdGhlIGRhdGEgc2NpZW5jZSBpbmR1c3RyeSBhbmQgaW4gdGhlIGFjYWRlbWljIG1hY2hpbmUgbGVhcm5pbmcgY29tbXVuaXR5LiBQeXRob24gbGlicmFyaWVzIGFyZSB0eXBpY2FsbHkgYnVpbHQgaW4gQy9DKysgb3IgRm9ydHJhbiBmb3IgZWZmaWNpZW50IGltcGxlbWVudGF0aW9ucy4KClIgaXMgYSBwcm9ncmFtbWluZyBsYW5ndWFnZSBkZXNpZ25lZCBzcGVjaWZpY2FsbHkgZm9yIHN0YXRpc3RpY2FsIGNvbXB1dGluZyBhbmQgZ3JhcGhpY3MuIExpa2UgUHl0aG9uLCBSJ3MgbWFpbiBzdHJlbmd0aCBpcyBpdHMgZXh0ZW5zaXZlIGNvbGxlY3Rpb24gb2Ygb3Blbi1zb3VyY2UgcGFja2FnZXMgY29udHJpYnV0ZWQgYnkgdGhlIGNvbW11bml0eSBvZiB1c2Vycywgd2hpY2ggbWFpbmx5IGNvbnNpc3RzIG9mIHN0YXRpc3RpY2lhbnMgYW5kIHJlc2VhcmNoZXJzLiBJdCBmZWF0dXJlcyBidWlsdC1pbiBmdW5jdGlvbnMgZm9yIHBvcHVsYXIgc3RhdGlzdGljYWwgYWxnb3JpdGhtcyBsaWtlIGxpbmVhciByZWdyZXNzaW9uIGFzIHdlbGwgYXMgZmxleGlibGUgZ3JhcGhpY2FsIGNhcGFiaWxpdGllcy4gVGhlcmUgYXJlIGFsc28gbWFueSBwYWNrYWdlcyBmb3IgY29tcGxleCBkYXRhIG1hbmlwdWxhdGlvbiBsaWtlIHRoZSBUaWR5dmVyc2UgY29sbGVjdGlvbiB3aGljaCBpbmNsdWRlcyBkcGx5ciBhbmQgdGhlIGhpZ2gtcXVhbGl0eSB2aXN1YWxpemF0aW9uIHRvb2wgZ2dwbG90Mi4gUidzIHN5bnRheCBpcyB0YWlsb3JlZCBmb3Igc3RhdGlzdGljYWwgYW5hbHlzaXMsIHVzaW5nIHZlY3RvcnMgYXMgaXRzIGRhdGEgc3RydWN0dXJlIGZvciBzdG9yaW5nIGFuZCBtYW5pcHVsYXRpbmcgZGF0YS4gSXQgaW5jbHVkZXMgbWFueSBidWlsdC1pbiByZXByb2R1Y2liaWxpdHkgZmVhdHVyZXMgbGlrZSBzZXR0aW5nIGEgc2VlZCBmb3IgcHNldWRvLXJhbmRvbSBudW1iZXIgZ2VuZXJhdG9ycy4gSXRzIHBhY2thZ2VzIGFyZSBhbHNvIHR5cGljYWxseSBpbXBsZW1lbnRlZCBpbiBDIG9yIEMrKy4KCgpPdGhlciBwb3B1bGFyIGhpZ2gtbGV2ZWwgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzIGluIGRhdGEgc2NpZW5jZSBpbmNsdWRlIEphdmEgYW5kIHJlbGF0aXZlIG5ld2NvbWVyIEp1bGlhLCBhbmQgY29tbW9uIHByb3ByaWV0YXJ5IHByb2dyYW1taW5nIGxhbmd1YWdlcyBpbmNsdWRlIFNBUywgU3RhdGEsIGFuZCBNQVRMQUIuCgojIFNldHVwCgpUaGlzIHByb2plY3QgY29udGFpbmVkIHRocmVlIHBoYXNlcywgd2hpY2ggYXJlIHJlcHJlc2VudGF0aXZlIG9mIGEgdHlwaWNhbCBkYXRhIHNjaWVuY2UgcHJvamVjdDogCgoxKSBTaW11bGF0aW5nIGRhdGE7CgoyKSBXcml0aW5nIGFuZCBydW5uaW5nIHZhcmlvdXMgYWxnb3JpdGhtcyBhbmQgcmVjb3JkaW5nIGV4ZWN1dGlvbiB0aW1lIGFuZCBtZW1vcnkgdXNhZ2U7CgozKSBBbmFseXppbmcgYW5kIHByZXNlbnRpbmcgdGhlIGRhdGEuCgoKIyMgU2ltdWxhdGlvbgoKSSBzaW11bGF0ZWQgYSBsaW5lYXIgcmVncmVzc2lvbiBkYXRhc2V0IGluIFIgd2l0aCAkbj0xMF42JCBhbmQgJHA9MTAkIGFzIGZvbGxvd3M6ClxiZWdpbnthbGlnbip9ClxiZXRhX2ogJlxvdmVyc2V0e2lpZH17XHNpbX0gXG1hdGhybXtVbmlmfSgwLDEwKSwgXHF1YWQgaj0xLFxkb3RzLCBwIFxcCnhfe2lqfSAmXG92ZXJzZXR7aWlkfXtcc2ltfSBcbWF0aGNhbHtOfSgwLDEpLCBccXVhZCBpPTEsIFxkb3RzLG4gXFwKeV9pICZcb3ZlcnNldHtpbmRlcC59e1xzaW19IFxtYXRoY2Fse059KDEwMCtcc3VtX3tqPTF9XntwfSBcYmV0YV9qIHhfe2lqfSwxKS4KXGVuZHthbGlnbip9ClRoZSAkeSQgdmVjdG9yIHdhcyB1c2VkIGJ5IHRoZSBsb29wIGFuZCBib290c3RyYXAgYWxnb3JpdGhtcywgYW5kIHRoZSBmdWxsICQoeSxYKSQgZGF0YXNldCB3YXMgdXNlZCBieSB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gYW5kIE1hcmtvdiBjaGFpbiBNb250ZSBDYXJsbyBhbGdvcml0aG1zLgoKSSBhbHNvIGdlbmVyYXRlZCBhIHZlY3RvciAKXGJlZ2lue2FsaWduKn0KSV9pICZcb3ZlcnNldHtpaWR9e1xzaW19IFxtYXRocm17QmVybn0oMS8yKSwgXHF1YWQgaT0xLCBcZG90cywgblxcCmZfaSAmPSAoLTEpXntJX2l9ClxlbmR7YWxpZ24qfQphbmQgdGhlIGRhdHNldCAkKGYsIFgpJCB3YXMgdXNlZCBieSB0aGUgc3VwcG9ydCB2ZWN0b3IgbWFjaGluZSBhbGdvcml0aG0uCgpUd28gJDEwMDBcdGltZXMgMTAwMCQgbWF0cmljZXMgd2VyZSBwb3B1bGF0ZWQgZnJvbSBhIHN0YW5kYXJkIE5vcm1hbCBkaXN0cmlidXRpb24uIFRoZXNlIG1hdHJpY2VzIHdlcmUgdXNlZCBpbiB0aGUgbWF0cml4IG11bHRpcGxpY2F0aW9uIGFuZCBpbnZlcnNpb24gb3BlcmF0aW9ucy4gVGhlIGRhdGEgJCh5LFgsZikkIHdhcyBzYXZlZCBhcyBhIG1hdHJpeCBpbiB0aGUgZmlsZSBEYXRhL2RhdGEuY3N2LCBhbmQgdGhlIHR3byBtYXRyaWNlcyAkQSQgYW5kICRCJCB3ZXJlIHNhdmVkIGluIHRoZSBmaWxlcyBEYXRhL0EuY3N2IGFuZCBEYXRhL0IuY3N2LiBUaGUgc2NyaXB0IHVzZWQgdG8gZ2VuZXJhdGUgdGhlIGRhdGEgaXMgYXZhaWxhYmxlIGluIHRoZSByZXBvc2l0b3J5IGh0dHBzOi8vZ2l0aHViLmNvbS9sZW92YW5jaXUvY3MzMl9maW5hbF9wcm9qZWN0IGF0IERhdGEvZGF0YS5SLCBhbmQgdGhlIGNzdiBmaWxlcyBmb3IgdGhlIG1hdHJpY2VzIGlzIGF2YWlsYWJsZSBpbiB0aGUgc2FtZSByZXBvc2l0b3J5IHdoaWxlIHRoZSBkYXRhLmNzdiBmaWxlIHdhcyB0b28gbGFyZ2UgdG8gYmUgdXBsb2FkZWQgdG8gR2l0aHViIGJ1dCB0aGUgc2FtZSBkYXRhIEkgdXNlZCBjYW4gYmUgZ2VuZXJhdGVkIHRocm91Z2ggdGhlIGZpeGVkIHNlZWQgc2V0IGluIHRoZSBSIGZpbGUuCgpUaGUgUiBjb2RlIHVzZWQgdG8gZ2VuZXJhdGUgdGhlIGRhdGEgaXMgdmVyeSBzdHJhaWdodGZvcndhcmQgZm9sbG93aW5nIHByb2JhYmlsaXN0aWMgbm90YXRpb24gYXMgZm9sbG93cy4KYGBge3IsIGV2YWwgPSBGQUxTRX0Kc2V0LnNlZWQoMzIpCiMgR2VuZXJhdGUgbGluZWFyIHJlZ3Jlc3Npb24gZGF0YQpiZXRhcyA8LSBydW5pZigxMCwgLTEwLCAxMCkKClggPC0gbWF0cml4KHJub3JtKDFlNiAqIDEwKSwgbmNvbCA9IDEwKQp5IDwtIDEwMCArIFhbLDE6MTBdICUqJSBiZXRhcyArIHJub3JtKDFlNikKZiA8LSBzYW1wbGUoYygtMSwgMSksIDFlNiwgcmVwbGFjZSA9IFRSVUUpCmRhdGEgPC0gY2JpbmQoeSwgWCwgZikKd3JpdGUuY3N2KGRhdGEsICJEYXRhL2RhdGEuY3N2IikKCiMgR2VuZXJhdGUgbWF0cml4IGRhdGEKQSA8LSBtYXRyaXgocnVuaWYoMWU2KSwgbmNvbCA9IDFlMykKQiA8LSBtYXRyaXgocnVuaWYoMWU2KSwgbmNvbCA9IDFlMykKd3JpdGUuY3N2KEEsICJEYXRhL0EuY3N2IikKd3JpdGUuY3N2KEIsICJEYXRhL0IuY3N2IikKYGBgCgoKCiMjIEFsZ29yaXRobXMKCkZvciBlYWNoIGFsZ29yaXRobSwgSSBtZWFzdXJlIGV4ZWN1dGlvbiB0aW1lIGFuZCBtZW1vcnkgdXNpbmcgdGhlICJiZW5jaG1hcmsiIHBhY2thZ2UgaW4gUiBhbmQgdGhlICJ0aW1lIiBhbmQgIm1lbW9yeV9wcm9maWxlciIgbGlicmFyaWVzIGluIFB5dGhvbi4gVGhlIG1lYXN1cmVkIHRpbWUgd2FzIHRvdGFsIHVzZXIgdGltZSByYXRoZXIgdGhhbiBwcm9jZXNzb3IgdGltZS4gVGhlIGFsZ29yaXRobXMgd2VyZSBtZWFudCB0byBiZSB3cml0dGVuIGluIGEgd2F5IHRoYXQgaXMgYXMgc3RydWN0dXJhbGx5IHNpbWlsYXIgYXMgcG9zc2libGUgaW4gYm90aCBsYW5ndWFnZXMsIGJ1dCB0aGUgaW1wbGVtZW50YXRpb25zIG9mIGJhc2UgZnVuY3Rpb25zIHZhcnkgZ3JlYXRseSBiZXR3ZWVuIHRoZSB0d28gbGFuZ3VhZ2VzLiBVbHRpbWF0ZWx5LCB0aGlzIGlzIHdoYXQgbWFrZXMgdGhlIGNvbXBhcmlzb24gaW50ZXJlc3Rpbmcgc2luY2UgdGhlcmUgY2FuIGJlIGdyZWF0IGRpZmZlcmVuY2VzIGluIGV4ZWN1dGlvbiB0aW1lIGZvciBzaW1pbGFyIG9wZXJhdGlvbnMsIGJ1dCBpdCBhbHNvIG1lYW5zIHRoYXQgYW4gYWxnb3JpdGhtIGNhbiBiZSB3cml0dGVuIGluIGEgcG9vcmx5IG9wdGltaXplZCB3YXkgaW4gYSBsYW5ndWFnZSBhbmQgYXJ0aWZpY2lhbGx5IHNlZW0gbXVjaCBmYXN0ZXIgaW4gdGhlIG90aGVyLiBGb3IgaW5zdGFuY2UsIHBlb3BsZSBvZnRlbiBjcml0aWNpemUgdGhlIHNwZWVkIG9mIGxvb3BzIGluIFIgYW5kIFB5dGhvbiBhbmQgbXkgdGVzdHMgYWdyZWUgd2l0aCB0aGlzIGNyaXRpY2lzbSwgYnV0IHRoZXNlIG9wZXJhdGlvbnMgY2FuIGFsbW9zdCBhbHdheXMgYmUgbWFkZSBtdWNoIGZhc3RlciBieSB1c2luZyB2ZWN0b3JpemVkIG9wZXJhdGlvbnMuCgpNZW1vcnkgdHVybmVkIG91dCBkaWZmaWN1bHQgdG8gbWVhc3VyZSwgYW5kIEkgcmVsaWVkIG9uIGV4dGVybmFsIHBhY2thZ2VzLiBSIGFuZCBQeXRob24gaW1wbGVtZW50IG1lbW9yeSBtYW5hZ2VtZW50IGluIHZlcnkgZGlmZmVyZW50IHdheXMuIEZvciBpbnN0YW5jZSwgUiBkaXN0aW5ndWlzaGVzIGJldHdlZW4gbWVtb3J5IHN0b3JlZCBieSB2ZWN0b3JzIGFuZCBieSBldmVyeXRoaW5nIGVsc2UuIEl0IGFwcGVhcnMgdGhhdCBtZW1vcnkgdXNhZ2Ugd2FzIHRvbyBsb3cgdG8gYmUgcmVjb3JkZWQgaW4gUiBmb3Igc2V2ZXJhbCBvcGVyYXRpb25zIHN1Y2ggYXMgbG9vcHMgYW5kIHdhcyByZWNvcmRlZCBhcyAwLiBUaGlzIGlzIGEgZmxvYXRpbmcgcG9pbnQgcHJvYmxlbSB0aGF0IEkgd2FzIG5vdCBhYmxlIHRvIHNvbHZlIGRlc3BpdGUgdHJ5aW5nIG1hbnkgYXBwcm9hY2hlcywgaW5jbHVkaW5nIHRoZSBiYXNlIFIgaW1wbGVtZW50YXRpb24gb2YgbWVtb3J5IG1vbml0b3JpbmcgYW5kIHNldmVyYWwgcGFja2FnZXMuIFRoZSBpbnRlcnByZXRhdGlvbiBvZiBtZW1vcnkgdXNhZ2UgaXMgYWxzbyBtdXJreSwgYXMgZGV0YWlsZWQgaW4gdGhlIFtEaWN1c3Npb25dKCNkaXNjdXNzaW9uKSBzZWN0aW9uLgoKVGhlIGZvbGxvd2luZyBhbGdvcml0aG1zIHdlcmUgdGVzdGVkOiBhIHN1bSBsb29wLCBhIGdlb21ldHJpYyBtZWFuIGxvb3AgYW5kIGEgdmVjdG9yaXplZCBpbXBsZW1lbnRhdGlvbiwgbWF0cml4IG11bHRpcGxpY2F0aW9uIGFuZCBpbnZlcnNpb24sIGxpbmVhciByZWdyZXNzaW9uLCB0aGUgYm9vdHN0cmFwLCB0d28gTWFya292IENoYWluIE1vbnRlIENhcmxvIChNQ01DKSBhbGdvcml0aG1zLCBhbmQgYSBzdXBwb3J0IHZlY3RvciBtYWNoaW5lIChTVk0pLiBXaGVuIHBvc3NpYmxlLCBJIGltcGxlbWVudGVkIGEgc2ltcGxlIHZlcnNpb24gb2YgdGhlc2UgYWxnb3JpdGhtcyB1c2luZyBvbmx5IGJhc2UgZnVuY3Rpb25zIGluIHRoZSBsYW5ndWFnZSBhcyB3ZWxsIGFzIGEgcG9wdWxhciBwYWNrYWdlL2xpYnJhcnkuIEZvciBpbnN0YW5jZSwgZm9yIGxpbmVhciByZWdyZXNzaW9uIEkgdXNlIHRoZSBsbSgpIGZ1bmN0aW9uIGluIFIgYW5kIHRoZSBTY2lraXQtTGVhcm4gbGlicmFyeSBpbiBQeXRob24gYXMgd2VsbCBhcyBhbiBhbGdvcml0aG0gdXNpbmcgb25seSBtYXRyaXggbXVsdGlwbGljYXRpb24gYW5kIGludmVyc2lvbi4gT2YgY291cnNlLCB0aGVzZSB0d28gbWF0cml4IG9wZXJhdGlvbnMgYXJlIHRoZW1zZWx2ZXMgYWxnb3JpdGhtcyB3aXRoIG1hbnkgZGlmZmVyZW50IHBvc3NpYmxlIGltcGxlbWVudGF0aW9ucy4gSSBtYWlubHkgdXNlIFIgaW4gbXkgZXZlcnlkYXkgbGlmZSBzbyBJIGFtIGxpa2VseSBtb3JlIGZhbWlsaWFyIHdpdGggdGhlIG1vc3Qgb3B0aW1pemVkIHBhY2thZ2VzIGluIFIgcmF0aGVyIHRoYW4gUHl0aG9uLgoKSSB2YXJpZWQgdGhlIHNhbXBsZSBzaXplIHdoZW4gcnVubmluZyB0aGUgYWxnb3JpdGhtcyBieSBzZWxlY3RpbmcgYSBzdWJzZXQgb2YgdGhlc2UgZGF0YXNldHMuIEkgdmFyaWVkIHRoZSBzYW1wbGUgc2l6ZSBmcm9tICRuPTEwXjIkIHRvICRuPTEwXjYkIGJ5IG1hZ25pdHVkZSBvZiAxMCBmb3IgdGhlIGxvb3AgYW5kIGxpbmVhciByZWdyZXNzaW9uIGFsZ29yaXRobXMuIEkgdmFyaWVkIHRoZSBzaXplIG9mIHRoZSBtYXRyaWNlcyBmcm9tICRuPTEwJCB0byAkbj0xMF4zJCBieSBtYWduaXR1ZGUgb2YgJDEwXnsxLzJ9JC4gSSBrZXB0IHRoZSBzYW1wbGUgc2l6ZSBvZiB0aGUgYm9vdHN0cmFwIGFuZCBNQ01DIGFsZ29yaXRobXMgZml4ZWQgYXQgJG49MTBeMyQgYW5kIHZhcmllZCB0aGUgc2FtcGxpbmcvcmVzYW1wbGluZyBzaXplIChzZWUgdGhlIHByZWNpc2UgZGVmaW5pdGlvbiBvZiB0aGUgYWxnb3JpdGhtcyBpbiB0aGUgW1Jlc3VsdHNdKCNyZXN1bHRzKSBzZWN0aW9uKSBmcm9tICRCPTEwJCB0byAkMTBeNSQgYnkgbWFnbml0dWRlIG9mIDEwLiBGb3IgdGhlIFNWTSBhbGdvcml0aG0sIEkgdmFyaWVkIHRoZSBzYW1wbGUgc2l6ZSBmcm9tICRuPTEwJCB0byAkbj0xMF4zJCBieSBtYWduaXR1ZGUgb2YgJDEwXnsxLzJ9JC4gVGhlIHNhbXBsZSBzaXplcyB3ZXJlIGNob3NlbiB0byByZXByZXNlbnQgdGhlIHNoaWZ0IGZyb20gdGhlIHR5cGljYWwgc2l6ZSBvZiBhIHN0YXRpc3RpY2FsIGFuYWx5c2lzIGFyb3VuZCAkbj0xMDAkIHRvIHRoZSBiaWcgZGF0YSBzaXplIG9mICRuPTEwXjYkIHdoaWNoIGFwcHJvYWNoZXMgdGhlIGxpbWl0cyBvZiBteSBtYWNoaW5lIGZvciBtYW55IGFsZ29yaXRobXMuIFRoZSBzYW1wbGUgc2l6ZSB3YXMgdmFyaWVkIGF0IGxvd2VyIHJhdGVzIGZvciBzb21lIGFsZ29yaXRobXMgYmVjYXVzZSBleGVjdXRpb24gdGltZSBpbmNyZWFzZWQgdG9vIHJhcGlkbHkgZm9yIHRoZSBib290c3RyYXAsIE1hcmtvdiBjaGFpbiBNb250ZSBDYXJsbyBhbGdvcml0aG1zLCBhbmQgdGhlIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmUuCgpJIHJhbiBzY3JpcHRzIGluIFIgYW5kIFB5dGhvbiBvbiB0aGVzZSBzaW11bGF0ZWQgZGF0YXNldHMgZm9yIDEwIGl0ZXJhdGlvbnMgYW5kIGZvciB0aGUgdmFyeWluZyBzYW1wbGUgc2l6ZXMgbWVudGlvbmVkIGFib3ZlLCBhbmQgSSByZWNvcmRlZCB0aGUgbWVkaWFuIGV4ZWN1dGlvbiB0aW1lIGFuZCBtZW1vcnkgdXNlIGluIENTViBmaWxlcyBpbiBSZXN1bHRzL3Jlc3VsdHNfUi5jc3YgYW5kIFJlc3VsdHMvcmVzdWx0c19weXRob24uY3N2LiBUaGUgc2NyaXB0cyB1c2VkIHRvIHJ1biB0aGUgYWxnb3JpdGhtcyBhcmUgYXZhaWxhYmxlIGluIHRoZSBHaXRIdWIgcmVwb3NpdG9yeSBhdCBTY3JpcHRzL2FsZ29yaXRobXMucHkgYW5kIFNjcmlwdHMvYWxnb3JpdGhtcy5SLiAKCiMjIEFuYWx5c2lzCgpJIHVzZWQgUiBNYXJrZG93biB0byBnZW5lcmF0ZSB0aGlzIEhUTUwgd2VicGFnZSBhbmQgdXNlZCB0aGUgcGxvdGx5IHBhY2thZ2UgdG8gZ2VuZXJhdGUgaW50ZXJhY3RpdmUgcGxvdHMgb2YgZXhlY3V0aW9uIHRpbWUgYW5kIG1lbW9yeSB1c2UgZm9yIGVhY2ggYWxnb3JpdGhtLiBJIGFsc28gZGlzY3VzcyB0aGVvcmV0aWNhbCBCaWctTyBjb21wbGV4aXR5IGFuZCBmaXQgYW4gZW1waXJpY2FsIGN1cnZlIHRvIHRoZSBwbG90cyBiYXNlZCBvbiB0aGUgbW9zdCBzaW1wbGUgdmVyc2lvbiBvZiB0aGVzZSBhbGdvcml0aG1zLgoKIyMgUmVwcm9kdWNpYmlsaXR5CkRldGFpbHMgb24gcmVwcm9kdWNpbmcgdGhlIGVudGlyZSBwcm9qZWN0IGNhbiBiZSBmb3VuZCBhdCBodHRwczovL2dpdGh1Yi5jb20vbGVvdmFuY2l1L2NzMzJfZmluYWxfcHJvamVjdC4gVGhlIGNvZGUgZm9yIHRoaXMgUm1hcmtkb3duIGZpbGUgY2FuIGJlIGRvd25sb2FkZWQgYnkgY2xpY2tpbmcgb24gdGhlIGNvZGUgYnV0dG9uIGF0IHRoZSB0b3Agb2YgdGhpcyB3ZWJwYWdlIG9yIGF0IGh0dHBzOi8vZ2l0aHViLmNvbS9sZW92YW5jaXUvbGVvdmFuY2l1LmdpdGh1Yi5pby9ibG9iL21hc3Rlci9jczMyLXByb2plY3QuUm1kLiBBbGwgdGhlIHNjcmlwdHMgZm9yIHNpbXVsYXRpbmcgdGhlIGRhdGEgYW5kIHJ1bm5pbmcgdGhlIHNjcmlwdHMgYXJlIGF2YWlsYWJsZSBpbiB0aGUgR2l0SHViIHJlcG9zaXRvcnkgYWJvdmUsIGFuZCBhbGwgdGhlIGNvZGUgdXNlZCB0byBnZW5lcmF0ZSBteSB3ZWJzaXRlIHdoaWNoIHdhcyBmb3JrZWQgZnJvbSBtbWlzdGFrZXMvbWluaW1hbC1taXN0YWtlcyBpcyBhdmFpbGFibGUgaW4gdGhlIEdpdGh1YiByZXBvc2l0b3J5IGh0dHBzOi8vZ2l0aHViLmNvbS9sZW92YW5jaXUvbGVvdmFuY2l1LmdpdGh1Yi5pby4KCkkgYW0gdXNpbmcgUiB2ZXJzaW9uIDQuMy4xIHdpdGggUlN0dWRpbyB2ZXJzaW9uIDIwMjMuMDkuMCs0NjMgYW5kIFB5dGhvbiB2ZXJzaW9uIDMuMTIuMyB3aXRoIFZpc3VhbCBTdHVkaW8gY29kZSB2ZXJzaW9uIDEuODguMS4gTXkgY29tcHV0ZXIgaXMgYSBNYWNCb29rIFBybyAoMTMtaW5jaCwgTTEsIDIwMjApIHdpdGggOCBHQiBvZiBtZW1vcnkgYW5kIHJ1bm5pbmcgb24gbWFjT1MgTW9udGVyZXkgVmVyc2lvbiAxMi4yLjEuCgojIFJlc3VsdHMgeyNyZXN1bHRzfSAKCiMjIERhdGEKQSBoaXN0b2dyYW0gb2YgdGhlIHNpbXVsYXRlZCBkYXRhIGZvciAkbj0xMF41JCBpcyBwbG90dGVkIGJlbG93LiBJdCBoYXMgbWVhbiAxMDAgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiAxNy4gQnkgY29uc3RydWN0aW9uLCBpdCBpcyBOb3JtYWxseSBkaXN0cmlidXRlZC4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGV2YWwgPSBUUlVFfQpsaWJyYXJ5KHBsb3RseSkKZGF0YSA8LSByZWFkLmNzdigiRGF0YS9kYXRhLmNzdiIpCmhpc3QgPC0gcGxvdF9seSh4ID0gZGF0YVsxOjFlNSwyXSwgdHlwZSA9ICJoaXN0b2dyYW0iKSAlPiUKICBsYXlvdXQodGl0bGUgPSAiU2ltdWxhdGVkIERhdGEiLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiRnJlcXVlbmN5IiksCiAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAieSIpKQpoaXN0CmBgYAoKCgojIyBMb29wIG9wZXJhdGlvbnMKCkZvciBsb29wcyBhcmUgdWJpcXVpdG91cyBwcm9ncmFtbWluZyBzdHJ1Y3R1cmVzIGluIGRhdGEgc2NpZW5jZSwgYW5kIHRoZXkgYXJlIHNvbWV3aGF0IGluZmFtb3VzIGluIGJvdGggUiBhbmQgUHl0aG9uLCBhcyB0aGV5IGFyZSBiZWxpZXZlZCB0byBiZSBtdWNoIHNsb3dlciB0aGFuIGluIGxvdy1sZXZlbCBsYW5ndWFnZXMgbGlrZSBDLgoKIyMjIExvb3Agc3VtcwoKSGVyZSBpcyBwc2V1ZG8gY29kZSBvZiBhIHNpbXBsZSBmb3JlYWNoIGxvb3AgdG8gY29tcHV0ZSBhIHN1bS4KCmBgYHBsYWludGV4dApGdW5jdGlvbiBsb29wX3N1bShuKQogICAgc3VtIDwtIDAKICAgIEZvciBpIGZyb20gMSB0byBuCiAgICAgICAgc3VtIDwtIHN1bSArIGkKICAgIEVuZCBGb3IKICAgIFJldHVybiBzdW0KRW5kIEZ1bmN0aW9uCmBgYAoKSW4gdGVybXMgb2YgY29tcHV0YXRpb25hbCBjb21wbGV4aXR5LCB0aGUgaW5pdGlhbGl6YXRpb24gc3RlcCBpcyAkTygxKSQsIGEgbGluZWFyIG9wZXJhdGlvbiBsaWtlIGEgc3VtIGlzIGFsc28gJE8oMSkkLCBhbmQgYnkgZGVmaW5pdGlvbiB0aGUgbG9vcCBpcyAkTyhuKSQgd2hpY2ggaXMgdGhlIG92ZXJhbGwgY29tcGxleGl0eSBvZiB0aGUgYWxnb3JpdGhtLiAKClRoZSBmb2xsb3dpbmcgY29kZSBzaG93cyB0aGUgaW1wbGVtZW50YXRpb24gaW4gUHl0aG9uLgpgYGB7cHl0aG9uLCBldmFsID0gRkFMU0V9CmRlZiBsb29wX3N1bShuKToKICAgIHN1bSA9IDAKICAgIGZvciBpIGluIHJhbmdlKG4pOgogICAgICAgIHN1bSArPSAxCiAgICByZXR1cm4gc3VtCmBgYAoKVGhlIGZvbGxvd2luZyBjb2RlIHNob3dzIHRoZSBpbXBsZW1lbnRhdGlvbiBpbiBSLgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbG9vcF9zdW0gPC0gZnVuY3Rpb24obikgewogIHN1bSA8LSAwCiAgZm9yIChpIGluIDE6bikgewogICAgc3VtIDwtIHN1bSArIDEKICB9CiAgcmV0dXJuKHN1bSkKfQpgYGAKCgpOb3RlIHRoYXQgdGhlIHN5bnRheCBmb3Igd3JpdGluZyBmdW5jdGlvbnMgaXMgc2xpZ2h0bHkgZGlmZmVyZW50IGluIFIgYW5kIFB5dGhvbi4gQm90aCBsYW5ndWFnZXMgdXNlIGZvcmVhY2ggbG9vcHMgd3JpdHRlbiBzaW1pbGFybHkuIFIgc3VwcG9ydHMgYm90aCA8LSBhbmQgPSBhcyBhc3NpZ25lbWVudCBvcGVyYXRvcnMuIEhpc3RvcmljYWxseSwgY29tZXMgZnJvbSB0aGUgUyBsYW5ndWFnZSB3aGljaCBhbHNvIHVzZXMgdGhlIDwtIGFzc2lnbm1lbnQgb3BlcmF0b3IuCgoKSSBpbmNsdWRlIGJlbG93IHRoZSBjb2RlIHVzZWQgdG8gZ2VuZXJhdGUgaW50ZXJhY3RpdmUgcGxvdHMgb2YgZXhlY3V0aW9uIHRpbWUgYW5kIG1lbW9yeSB1c2FnZSB3aXRoIHRoZSBwbG90bHkgbGlicmFyeSBpbiBSLiBHZW5lcmF0aW5nIHRoaXMgcGxvdCByZXF1aXJlZCBxdWl0ZSBhIGJpdCBvZiBkYXRhIG1hbmlwdWxhdGlvbiwgd2hpY2ggd2FzIGVhc2lseSBkb25lIGluIGJhc2UgUiwgYW5kIG1vcmUgY29tcGxleCBvcGVyYXRpb25zIGNhbiBiZSBkb25lIHdpdGggc2ltcGxlIHN5bnRheCB3aXRoIHRoZSBkcGx5ciBwYWNrYWdlLCB3aGljaCBJIHVzZWQgd2hlbiBzdG9yaW5nIHRoZSByZXN1bHRzIGZyb20gdGhlIGJlbmNobWFya3MuIEFsbCBvdGhlciBwbG90cyB3ZXJlIGdlbmVyYXRlZCB3aXRoIHNpbWlsYXIgY29kZSBoaWRkZW4gZm9yIHJlYWRhYmlsaXR5LgoKCmBgYHtyLG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkocGxvdGx5KQojIFJlYWQgZGF0YQpSIDwtIHJlYWQuY3N2KCIvVXNlcnMvYW5jYXZhbmNpdXBvcGVzY3UvRGVza3RvcC9DbGFzc2VzL0NTIDMyL0ZpbmFsIHByb2plY3QvUmVzdWx0cy9SZXN1bHRzX1IuY3N2IikKcHl0aG9uIDwtIHJlYWQuY3N2KCIvVXNlcnMvYW5jYXZhbmNpdXBvcGVzY3UvRGVza3RvcC9DbGFzc2VzL0NTIDMyL0ZpbmFsIHByb2plY3QvUmVzdWx0cy9SZXN1bHRzX3B5dGhvbi5jc3YiKQptZXJnZWRfZGF0YSA8LSBtZXJnZShSLCBweXRob24sIGJ5PWMoIkFsZ29yaXRobSIsICJuIiksIHN1ZmZpeGVzID0gYygiUiIsICJweXRob24iKSkKYWxnb3JpdGhtX25hbWVzIDwtIGMoJ2xvb3Bfc3VtJywnbG9vcF9nZW9tX21lYW4nLCAndmVjdG9yaXplZF9nZW9tX21lYW4nLCAnbWF0cml4X211bHRpcGxpY2F0aW9uJywgCiAgICAgICAgICAgICAgICAgJ21hdHJpeF9pbnZlcnNpb24nLCAnbGluZWFyX3JlZ3Jlc3Npb25fcGFja2FnZScsICdsaW5lYXJfcmVncmVzc2lvbl9iYXNlJywgCiAgICAgICAgICAgICAgICAgJ2Jvb3RzdHJhcF9wYWNrYWdlJywgJ2Jvb3RzdHJhcF9iYXNlJywgJ3N2bV9wYWNrYWdlJywgJ3N2bV9iYXNlJywgCiAgICAgICAgICAgICAgICAgJ01ldHJvcG9saXNfSGFzdGluZ3MnLCAnTUNNQ19zdGFuJykKCiMgQ29tcHV0ZSBCaWctTyBjb21wbGV4aXR5CmFsZ29zIDwtIGMoImxvb3Bfc3VtIikgIyBXZSBvbmx5IHBsb3Qgb25lIGFsZ29yaXRobSBoZXJlIGJ1dCBpbiBvdGhlciBwbG90cyBJIGhhdmUgbXVsdGlwbGUgYWxnb3JpdGhtcwpuX3ZhbHVlcyA8LSBzZXEoMiwgNiwgYnkgPSAwLjEpCk9fbiA8LSAxMF5uX3ZhbHVlcy8xMF40Ck9fbl9weXRob25fMSA8LSBPX24qcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSRUaW1lWzNdICMgVGhlIHByZS1mYWN0b3JzIGFyZSBzdGFuZGFyZGl6ZWQgb24gdGhlIGVtcGlyaWNhbCB0aW1lIHdpdGggbj0xMF40Ck9fbl9SXzEgPC0gT19uKlJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSRUaW1lWzNdCgojIFRpbWUgcGxvdApwbG90X2x5KCkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKG4sIDEwKSwgeSA9IH5UaW1lLCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdSJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIGRhc2ggPSAnc29saWQnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJibHVlIiwgc3ltYm9sID0gJ2NpcmNsZScpKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IHB5dGhvbltweXRob24kQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKG4sIDEwKSwgeSA9IH5UaW1lLCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdsb29wX3N1bSBQeXRob24nLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCBkYXNoID0gJ3NvbGlkJyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAicmVkIiwgc3ltYm9sID0gJ2RvdCcpKSAlPiUKICBhZGRfdHJhY2UoeCA9IG5fdmFsdWVzLCB5ID0gT19uX3B5dGhvbl8xLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywKICAgICAgICAgICAgbmFtZSA9ICdPKG4pJywgbGluZSA9IGxpc3QoY29sb3IgPSAnYmxhY2snLCBkYXNoID0gJ2Rhc2gnKSkgJT4lCiAgYWRkX3RyYWNlKHggPSBuX3ZhbHVlcywgeSA9IE9fbl9SXzEsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLAogICAgICAgICAgICBuYW1lID0gJ08obiknLCBsaW5lID0gbGlzdChjb2xvciA9ICdibGFjaycsIGRhc2ggPSAnZGFzaCcpLCBzaG93bGVnZW5kID0gRkFMU0UpICU+JQogIGxheW91dCh0aXRsZSA9ICJFeGVjdXRpb24gVGltZSIsCiAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJsb2cobikiLCB0aWNrbW9kZSA9ICJhcnJheSIsIHRpY2t2YWxzID0gMDo2LCB0aWNrdGV4dCA9IGFzLmNoYXJhY3RlcigwOjYpKSwKICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlRpbWUgKHMpIiksCiAgICAgICAgIGxlZ2VuZCA9IGxpc3QodGl0bGUgPSAiTGVnZW5kIiwgb3JpZW50YXRpb24gPSAiaCIsIHggPSAwLjMsIHkgPSAtMC4yKSkKYGBgCgoKYGBge3IgZWNobz1GQUxTRX0KIyBNZW1vcnkgcGxvdApwbG90X2x5KCkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKG4sIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ2xvb3Bfc3VtIFInLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibHVlIiwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBzeW1ib2wgPSAnY2lyY2xlJykpICU+JQogIGFkZF90cmFjZShkYXRhID0gcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSwgeCA9IH5sb2cobiwgMTApLCB5ID0gfk1lbW9yeSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnbG9vcF9zdW0gUHl0aG9uJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAicmVkIiwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gInJlZCIsIHN5bWJvbCA9ICdkb3QnKSkgJT4lCiAgbGF5b3V0KHRpdGxlID0gIk1lbW9yeSBVc2FnZSIsCiAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJsb2cobikiLCB0aWNrbW9kZSA9ICJhcnJheSIsIHRpY2t2YWxzID0gMDo2LCB0aWNrdGV4dCA9IGFzLmNoYXJhY3RlcigwOjYpKSwKICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIk1lbW9yeSAoTWlCKSIpLAogICAgICAgICBsZWdlbmQgPSBsaXN0KHRpdGxlID0gIkxlZ2VuZCIsIG9yaWVudGF0aW9uID0gImgiLCB4ID0gMC4zLCB5ID0gLTAuMikpCmBgYAoKCldlIHNlZSB0aGF0IGxvb3BzIGluIFIgcnVuIHR3byB0byB0aHJlZSB0aW1lcyBmYXN0ZXIgdGhhbiBpbiBQeXRvbi4gV2Ugc2VlIHRoYXQgJE8obikkIGZpdHMgdGhlIGRhdGEgcGVyZmVjdGx5LiBJdCBzZWVtcyB0aGF0IG1lbW9yeSB1c2FnZSB3YXMgdG9vIGxvdyB0byBiZSByZWNvcmRlZCBpbiBSIGFuZCB0aGVyZSBpcyBubyBjbGVhciB0cmVuZCBvbiB0aGUgbWVtb3J5IHVzYWdlIGluIFB5dGhvbiwgYWx0aG91Z2ggaXQgUHl0aG9uIGRvZXMgdXNlIGEgc3RyYW5nZWx5IGxhcmdlIGFtb3VudCBvZiBtZW1vcnkgZm9yIHN1Y2ggYSBzaW1wbGUgZm9yIGxvb3AgYWxnb3JpdGhtLiBUaGlzIGlzIGEgd2luIGZvciBleGVjdXRpb24gdGltZSBmb3IgUiwgYnV0IGl0IGlzIHdlbGwta25vd24gdGhhdCBsb29wcyBhcmUgc2xvdyBpbiBib3RoIFIgYW5kIFB5dGhvbiBkdWUgdG8gdGhlIG1hbnkgYWJzdHJhY3Rpb25zIG1hZGUgYnkgdGhlIHByb2dyYW1taW5nIGxhbmd1YWdlcy4gSXQgaXMgb2Z0ZW4gc3VnZ2VzdGVkIHRvIHZlY3Rvcml6ZSBvcGVyYXRpb25zLCB3aGljaCBjYW4gc3BlZWQgdXAgbG9vcHMgY29uc2lkZXJhYmx5LiAKCiMjIyBWZWN0b3JpemVkIG9wZXJhdGlvbnMKCgpWZWN0b3JpemVkIG9wZXJhdGlvbnMgYXJlIG9wZXJhdGlvbnMgYXBwbGllZCB0byBldmVyeSBlbnRyeSBvZiBhbiBhcnJheS4gVGhleSBhcmUgc3RpbGwgbG9vcHMsIGJ1dCB0aGV5IGFyZSB0eXBpY2FsbHkgY29tcGlsZWQgZGlyZWN0bHkgaW4gQyB3aGljaCBpcyBtdWNoIGZhc3RlciB0aGFuIGV4ZWN1dGluZyBhIGxvb3AgaW4gUiBvciBQeXRob24uIFRoZSBjb21wdXRhdGlvbmFsIGNvbXBsZXhpdHkgaXMgdGhlb3JldGljYWxseSBzaG91bGQgc3RpbGwgYmUgdGhlIHNhbWUgYXMgYSBsb29wIHNpbmNlIHRoZSBzYW1lIG9wZXJhdGlvbnMgYXJlIHBlcmZvcm1lZCwgYnV0IHRoZSBwcmUtZmFjdG9yIGlzIHJlZHVjZWQuIEluIGFkZGl0aW9uLCB0aGUgb3BlcmF0aW9ucyBjYW4gYmUgb3B0aW1pemVkIGZvciB0aGUgQ1BVIHRvIHBlcmZvcm0gbXVsdGlwbGUgb3BlcmF0aW9ucyBvbiB0aGUgZGF0YS4gSGVuY2UsIGR1ZSB0byBtb3JlIGVmZmljaWVudCBtZW1vcnkgYWxsb2NhdGlvbiBpdCBpcyBwb3NzaWJsZSBmb3IgdGhlIG9yZGVyIG9mIGV4ZWN1dGlvbiB0aW1lIHRvIGJlIHJlZHVjZWQgYXMgd2VsbC4gSW4gdGhlIGZvbGxvd2luZyBwbG90cywgSSBjb21wYXJlIHRoZSBjb21wdXRhdGlvbiBvZiB0aGUgZ2VvbWV0cmljIG1lYW4gY29tcHV0ZWQgYXMgJCRcZXhwXGxlZnQoXGZyYWN7MX17bn0gXHN1bV97aT0xfV5uIFxsb2cgeV9pXHJpZ2h0KSQkIHRocm91Z2ggYSBsb29wIGFuZCB3aXRoIGEgdmVjdG9yaXplZCBvcGVyYXRpb24uCgpSIGhhcyBhIGRpc3RpbmN0IHZlY3RvciBvYmplY3QgdHlwZSwgd2hpbGUgaW4gUHl0aG9uIHRoZXkgYXJlIHNpbXBseSBvbmUtZGltZW5zaW9uYWwgYXJyYXlzLiBUaGlzIGlzIGltcGxlbWVudGVkIGluIFB5dGhvbiB0aHJvdWdoIGxpYnJhcmllcyBzdWNoIGFzIG51bXB5IGFuZCBuYXRpdmVseSBpbiBSIGFsdGhvdWdoIHRoZXJlIGFyZSBhbHNvIHNldmVyYWwgcGFja2FnZXMgaW1wbGVtZW50aW5nIGVmZmljaWVudCB2ZWN0b3JpemVkIG9wZXJhdGlvbnMuIEhlcmUgaXMgdGhlIGNvZGUgaW4gUHl0aG9uOgpgYGB7cHl0aG9uLCBldmFsID0gRkFMU0V9CmRlZiB2ZWN0b3JpemVkX2dlb21fbWVhbihkYXRhKToKICAgIHJldHVybiBucC5leHAobnAuc3VtKG5wLmxvZyhkYXRhKSkgLyBsZW4oZGF0YSkpCmBgYAoKYW5kIGluIFI6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQp2ZWN0b3JpemVkX2dlb21fbWVhbiA8LSBmdW5jdGlvbihkYXRhKSB7CiAgcmV0dXJuKGV4cChtZWFuKGxvZyhkYXRhKSkpKQp9CmBgYAoKCkhlcmUgYXJlIHBsb3RzIHNob3dpbmcgZXhlY3V0aW9uIHRpbWUgYW5kIG1lbW9yeSBmb3IgcGxvdHMgYW5kIHZlY3Rvcml6ZWQgb3BlcmF0aW9ucyBpbiBSIGFuZCBQeXRob24uCgoKYGBge3IgZWNobz1GQUxTRX0KYWxnb3MgPC0gYygibG9vcF9nZW9tX21lYW4iLCAidmVjdG9yaXplZF9nZW9tX21lYW4iKQpwbG90X2x5KCkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKG4sIDEwKSwgeSA9IH5UaW1lLCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdsb29wX2dlb21fbWVhbiBSJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIGRhc2ggPSAnc29saWQnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJibHVlIiwgc3ltYm9sID0gJ2NpcmNsZScpKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IFJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSwgeCA9IH5sb2cobiwgMTApLCB5ID0gflRpbWUsIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ3ZlY3Rvcml6ZWRfZ2VvbV9tZWFuIFInLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibHVlIiwgZGFzaCA9ICdkYXNoZG90JyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIHN5bWJvbCA9ICdzcXVhcmUnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdLCB4ID0gfmxvZyhuLCAxMCksIHkgPSB+VGltZSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnbG9vcF9nZW9tX21lYW4gUHl0aG9uJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAicmVkIiwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gInJlZCIsIHN5bWJvbCA9ICdkb3QnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdLCB4ID0gfmxvZyhuLCAxMCksIHkgPSB+VGltZSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAndmVjdG9yaXplZF9nZW9tX21lYW4gUHl0aG9uJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAicmVkIiwgZGFzaCA9ICdkYXNoZG90JyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAicmVkIiwgc3ltYm9sID0gJ3NxdWFyZScpKSAlPiUKICBsYXlvdXQodGl0bGUgPSAiRXhlY3V0aW9uIFRpbWUiLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAibG9nKG4pIiwgdGlja21vZGUgPSAiYXJyYXkiLCB0aWNrdmFscyA9IDA6NiwgdGlja3RleHQgPSBhcy5jaGFyYWN0ZXIoMDo2KSksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJUaW1lIChzKSIpLAogICAgICAgICBsZWdlbmQgPSBsaXN0KHRpdGxlID0gIkxlZ2VuZCIsIG9yaWVudGF0aW9uID0gImgiLCB4ID0gMC4zLCB5ID0gLTAuMikpCgpwbG90X2x5KCkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKG4sIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ2xvb3BfZ2VvbV9tZWFuIFInLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibHVlIiwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBzeW1ib2wgPSAnY2lyY2xlJykpICU+JQogIGFkZF90cmFjZShkYXRhID0gUltSJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdLCB4ID0gfmxvZyhuLCAxMCksIHkgPSB+TWVtb3J5LCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICd2ZWN0b3JpemVkX2dlb21fbWVhbiBSJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIGRhc2ggPSAnZGFzaGRvdCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGFkZF90cmFjZShkYXRhID0gcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSwgeCA9IH5sb2cobiwgMTApLCB5ID0gfk1lbW9yeSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnbG9vcF9nZW9tX21lYW4gUHl0aG9uJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAicmVkIiwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gInJlZCIsIHN5bWJvbCA9ICdkb3QnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdLCB4ID0gfmxvZyhuLCAxMCksIHkgPSB+TWVtb3J5LCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICd2ZWN0b3JpemVkX2dlb21fbWVhbiBQeXRob24nLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCBkYXNoID0gJ2Rhc2hkb3QnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJyZWQiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGxheW91dCh0aXRsZSA9ICJNZW1vcnkgdXNhZ2UiLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAibG9nKG4pIiwgdGlja21vZGUgPSAiYXJyYXkiLCB0aWNrdmFscyA9IDA6NiwgdGlja3RleHQgPSBhcy5jaGFyYWN0ZXIoMDo2KSksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJNZW1vcnkgKE1pQikiKSwKICAgICAgICAgbGVnZW5kID0gbGlzdCh0aXRsZSA9ICJMZWdlbmQiLCBvcmllbnRhdGlvbiA9ICJoIiwgeCA9IDAuMywgeSA9IC0wLjIpKQpgYGAKCgpXZSBzZWUgdGhhdCBsb29wcyBpbiBQeXRob24gYW5kIFIgYXJlIHZlcnkgc2xvdyBjb21wYXJlZCB0byB0aGUgdmVjdG9yaXplZCBvcGVyYXRpb24uIEFsdGhvdWdoIHRoZSBwcmUtZmFjdG9yIGlzIG11Y2ggc21hbGxlciBmb3IgdmVjdG9yaXplZCBvcGVyYXRpb25zLCB0aGUgY29tcHV0YXRpb25hbCBjb21wbGV4aXR5IGlzIHN0aWxsICRPKG4pJCwgYXMgY2FuIGJlIHNlZW4gYnkgZml0dGluZyBhIGxpbmVhciBjdXJ2ZSB0byB0aGUgcGxvdCBvZiB2ZWN0b3JpemVkIG9wZXJhdGlvbnMgYmVsb3cuCgoKCmBgYHtyLCBlY2hvPUZBTFNFfQphbGdvcyA8LSBjKCJ2ZWN0b3JpemVkX2dlb21fbWVhbiIpCm5fdmFsdWVzIDwtIHNlcSgyLCA2LCBieSA9IDAuMSkKT19uIDwtIDEwXm5fdmFsdWVzLzEwXjQKT19uX3B5dGhvbl8xIDwtIE9fbipweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdJFRpbWVbM10KT19uX1JfMSA8LSBPX24qUltSJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdJFRpbWVbM10KcGxvdF9seSgpICU+JQogIGFkZF90cmFjZShkYXRhID0gUltSJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdLCB4ID0gfmxvZyhuLCAxMCksIHkgPSB+VGltZSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAndmVjdG9yaXplZF9nZW9tX21lYW4gUHl0aG9uJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIGRhc2ggPSAnZGFzaGRvdCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGFkZF90cmFjZShkYXRhID0gcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSwgeCA9IH5sb2cobiwgMTApLCB5ID0gflRpbWUsIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ3ZlY3Rvcml6ZWRfZ2VvbV9tZWFuIFB5dGhvbicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIsIGRhc2ggPSAnZGFzaGRvdCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gInJlZCIsIHN5bWJvbCA9ICdzcXVhcmUnKSkgJT4lCiAgYWRkX3RyYWNlKHggPSBuX3ZhbHVlcywgeSA9IE9fbl9SXzEsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLAogICAgICAgICAgICBuYW1lID0gJ08obiknLCBsaW5lID0gbGlzdChjb2xvciA9ICdibGFjaycsIGRhc2ggPSAnZGFzaCcpKSAlPiUKICBsYXlvdXQodGl0bGUgPSAiRXhlY3V0aW9uIFRpbWUiLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAibG9nKG4pIiwgdGlja21vZGUgPSAiYXJyYXkiLCB0aWNrdmFscyA9IDA6NiwgdGlja3RleHQgPSBhcy5jaGFyYWN0ZXIoMDo2KSksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJUaW1lIChzKSIpLAogICAgICAgICBsZWdlbmQgPSBsaXN0KHRpdGxlID0gIkxlZ2VuZCIsIG9yaWVudGF0aW9uID0gImgiLCB4ID0gMC4zLCB5ID0gLTAuMikpCmBgYAoKCgoKIyMgTWF0cml4IG9wZXJhdGlvbnMKCgpNYXRyaXggb3BlcmF0aW9ucywgbmFtZWx5IGFkZGl0aW9uLCBzdWJ0cmFjdGlvbiwgbXVsdGlwbGljYXRpb24sIGludmVyc2lvbiwgYW5kIGRvdCBwcm9kdWN0cyBhcmUgY2VudHJhbCB0byBzdGF0aXN0aWNzIHBhcnRpY3VsYXJseSBmb3IgbXVsdGl2YXJpYXRlIGRhdGEuIEZvciBpbnN0YW5jZSwgbGluZWFyIHJlZ3Jlc3Npb24gY2FuIGJlIGZyYW1lZCBlbnRpcmVseSBhcyBhbiBhbGdvcml0aG0gaW52b2x2aW5nIG9ubHkgbWF0cml4IG11bHRpcGxpY2F0aW9uIGFuZCBpbnZlcnNpb24uIEhlcmUsIEkgdGVzdCB0aGUgaW1wbGVtZW50YXRpb24gb2YgbWF0cml4IG11bHRpcGxpY2F0aW9uIGFuZCBpbnZlcnNpb24gaW4gUHl0aG9uIGFuZCBSLiBUaGlzIGlzIGEgb25lLWxpbmVyIGluIGJvdGggbGFuZ3VhZ2VzLCBidXQgdGhlIGFjdHVhbCBhbGdvcml0aG1zIGZvciBtYXRyaXggbXVsdGlwbGljYXRpb24gYW5kIGludmVyc2lvbiBhcmUgYWN0dWFsbHkgcXVpdGUgY29tcHV0YXRpb25hbGx5IGNvc3RseS4KCkZvciBtYXRyaXggbXVsdGlwbGljYXRpb24sIHRoZSBtYXRoZW1hdGljYWwgZGVmaW5pdGlvbiBnaXZlcyB0aGUgZm9sbG93aW5nIGFsZ29yaXRobS4KCmBgYHBsYWludGV4dApGdW5jdGlvbiBtdXRpcGx5X21hdHJpY2VzKEEsIEIpCiAgICAvLyBEaW1lbnNpb25zCiAgICBtIDwtIG51bWJlciBvZiByb3dzIGluIEEKICAgIG4gPC0gbnVtYmVyIG9mIGNvbHVtbnMgaW4gQQogICAgcCA8LSBudW1iZXIgb2YgY29sdW1ucyBpbiBCCiAgICBDIDwtIEluaXRpYWxpemUgd2l0aCB6ZXJvcwogICAgCiAgICBGb3IgaSBmcm9tIDEgdG8gbQogICAgICAgIEZvciBqIGZyb20gMSB0byBwCiAgICAgICAgICAgIHN1bSA8LSAwCiAgICAgICAgICAgIEZvciBrIGZyb20gMSB0byBuCiAgICAgICAgICAgICAgICBzdW0gPC0gc3VtICsgQVtpXVtrXSAqIEJba11bal0KICAgICAgICAgICAgRW5kRm9yCiAgICAgICAgICAgIENbaV1bal0gPC0gc3VtCiAgICAgICAgRW5kRm9yCiAgICBFbmRGb3IKICAgIAogICAgUmV0dXJuIEMKRW5kIEZ1bmN0aW9uCmBgYAoKRm9yIGVhY2ggcGFpciBvZiAkbSQgcm93IGZyb20gJEEkIGFuZCAkcCQgY29sdW1uIGZyb20gJEIkLCAkbiQgbXVsdGlwbGljYXRpb25zIGFyZSBwZXJmb3JtZWQgdGhyb3VnaCB0aHJlZSBuZXN0ZWQgbG9vcHMuIEZvciBlYWNoIG9mIHRoZSAkbiQgbXVsdGlwbGljYXRpb25zLCB0aGVyZSBpcyBhIHN1bSB3aGljaCBoYXMgY29tcGxleGl0eSAkTygxKSQuIFRoaXMgZ2l2ZXMgY29tcHV0YXRpb25hbCBjb21wbGV4aXR5ICRcbWF0aGNhbHtPfShtbnApJCBhbmQgaW4gdGhlIGNhc2Ugd2hlcmUgJG09bj1wJCB0aGlzIGlzICRPKG5eMykkLiBIb3dldmVyLCBQeXRob24gYW5kIFIgbGlrZWx5IGhhdmUgYSBtb3JlIGVmZmljaWVudCB2ZXJzaW9uIHRocm91Z2ggc29tZSBjbGV2ZXIgYWxnb3JpdGhtLiAKCgpIZXJlIGlzIGJhc2ljIHBzZXVkb2NvZGUgZm9yIHRoZSBHYXVzcy1Kb3JkYW4gYWxnb3JpdGhtIGZvciBtYXRyaXggaW52ZXJzaW9uLgpgYGBwbGFpbnRleHQKRnVuY3Rpb24gaW52ZXJ0X21hdHJpeChBKQogICAgLy8gRGltZW5zaW9ucwogICAgbiA8LSBudW1iZXIgb2Ygcm93cyBpbiBBCiAgICBCIDwtIEluaXRpYWxpemUgbWF0cml4IEIgYXMgYW4gaWRlbnRpdHkgbWF0cml4CiAgICAKICAgIC8vIFBlcmZvcm0gR2F1c3NpYW4gZWxpbWluYXRpb24KICAgIEZvciBpIGZyb20gMSB0byBuCiAgICAgICAgZGl2aXNvciA8LSBBW2ldW2ldCiAgICAgICAgRm9yIGogZnJvbSAxIHRvIG4KICAgICAgICAgICAgQVtpXVtqXSA8LSBBW2ldW2pdIC8gZGl2aXNvcgogICAgICAgICAgICBCW2ldW2pdIDwtIEJbaV1bal0gLyBkaXZpc29yCiAgICAgICAgRW5kRm9yCiAgICAgICAgCiAgICAgICAgLy8gU2V0IG90aGVyIGVsZW1lbnRzIGluIGNvbHVtbiBpIG9mIEEgdG8gMAogICAgICAgIEZvciBrIGZyb20gMSB0byBuCiAgICAgICAgICAgIElmIGsgIT0gaQogICAgICAgICAgICAgICAgZmFjdG9yIDwtIEFba11baV0KICAgICAgICAgICAgICAgIEZvciBqIGZyb20gMSB0byBuCiAgICAgICAgICAgICAgICAgICAgQVtrXVtqXSA8LSBBW2tdW2pdIC0gZmFjdG9yICogQVtpXVtqXQogICAgICAgICAgICAgICAgICAgIEJba11bal0gPC0gQltrXVtqXSAtIGZhY3RvciAqIEJbaV1bal0KICAgICAgICAgICAgICAgIEVuZEZvcgogICAgICAgICAgICBFbmRJZgogICAgICAgIEVuZEZvcgogICAgRW5kRm9yCiAgICAKICAgIFJldHVybiBCCkVuZCBGdW5jdGlvbgpgYGAKCkxpa2V3aXNlIHRvIG1hdHJpeCBtdWx0aXBsaWNhdGlvbiwgdGhlcmUgYXJlIHRocmVlIG5lc3RlZCBsb29wcyB3aGljaCBnaXZlcyBjb21wbGV4aXR5ICRcbWF0aGNhbHtPfShuXjMpJC4gCgpJbiBQeXRob24gdGhpcyBpcyBzaW1wbHkgaW1wbGVtZW50ZWQgYXMgZm9sbG93cy4KYGBge3B5dGhvbiwgZXZhbCA9IEZBTFNFfQpkZWYgbXVsdGlwbHlfbWF0cmljZXMoQSwgQik6CiAgICByZXR1cm4gQSBAIEIKCmRlZiBpbnZlcnRfbWF0cml4KEEpOgogICAgcmV0dXJuIG5wLmxpbmFsZy5pbnYoQSkKYGBgCgpMaWtld2lzZSBpbiBSIHRoaXMgaXMgc2ltcGx5IGltcGxlbWVudGVkIGFzIGZvbGxvd3MuIFNpZGVub3RlOiBJIHJlYWxseSBkbyBub3QgbGlrZSB0aGUgc3ludGF4ICUqJSBmb3IgbWF0cml4IG11bHRpcGxpY2F0aW9uIGluIFIuCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQptdWx0aXBseV9tYXRyaWNlcyA8LSBmdW5jdGlvbihBLCBCKSB7CiAgcmV0dXJuKEEgJSolIEIpCn0KCmludmVydF9tYXRyaXggPC0gZnVuY3Rpb24oQSkgewogIHJldHVybihzb2x2ZShBKSkKfQpgYGAKCgpgYGB7ciBlY2hvPUZBTFNFfQphbGdvcyA8LSBjKCJtYXRyaXhfbXVsdGlwbGljYXRpb24iLCAibWF0cml4X2ludmVyc2lvbiIpCm5fdmFsdWVzIDwtIHNlcSgxLCAzLCBieSA9IDAuMSkKT19uIDwtIDEwXm5fdmFsdWVzLzEwXjIKT19uMyA8LSAoMTBebl92YWx1ZXMvMTBeMileMwpPX25fcHl0aG9uXzEgPC0gT19uKnB5dGhvbltweXRob24kQWxnb3JpdGhtID09IGFsZ29zWzFdLF0kVGltZVszXQpPX24zX1JfMSA8LSBPX24zKlJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSRUaW1lWzNdCk9fbl9weXRob25fMiA8LSBPX24qcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSRUaW1lWzNdCk9fbjNfUl8yIDwtIE9fbjMqUltSJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdJFRpbWVbM10KcGxvdF9seSgpICU+JQogIGFkZF90cmFjZShkYXRhID0gUltSJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdLCB4ID0gfmxvZyhzcXJ0KG4pLCAxMCksIHkgPSB+VGltZSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnbWF0cml4X211bHRpcGxpY2F0aW9uIFInLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibHVlIiwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBzeW1ib2wgPSAnY2lyY2xlJykpICU+JQogIGFkZF90cmFjZShkYXRhID0gUltSJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdLCB4ID0gfmxvZyhzcXJ0KG4pLCAxMCksIHkgPSB+VGltZSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnbWF0cml4X2ludmVyc2lvbiBSJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIGRhc2ggPSAnZGFzaGRvdCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGFkZF90cmFjZShkYXRhID0gcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSwgeCA9IH5sb2coc3FydChuKSwgMTApLCB5ID0gflRpbWUsIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ21hdHJpeF9tdWx0aXBsaWNhdGlvbiBQeXRob24nLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCBkYXNoID0gJ3NvbGlkJyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAicmVkIiwgc3ltYm9sID0gJ2RvdCcpKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IHB5dGhvbltweXRob24kQWxnb3JpdGhtID09IGFsZ29zWzJdLF0sIHggPSB+bG9nKHNxcnQobiksIDEwKSwgeSA9IH5UaW1lLCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdtYXRyaXhfaW52ZXJzaW9uIFB5dGhvbicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIsIGRhc2ggPSAnZGFzaGRvdCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gInJlZCIsIHN5bWJvbCA9ICdzcXVhcmUnKSkgJT4lCiAgYWRkX3RyYWNlKHggPSBuX3ZhbHVlcywgeSA9IE9fbl9weXRob25fMSwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycsCiAgICAgICAgICAgIG5hbWUgPSAnTyhuKScsIGxpbmUgPSBsaXN0KGNvbG9yID0gJ2JsYWNrJywgZGFzaCA9ICdkYXNoJykpICU+JQogIGFkZF90cmFjZSh4ID0gbl92YWx1ZXMsIHkgPSBPX24zX1JfMiwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycsCiAgICAgICAgICAgIG5hbWUgPSAnTyhuXjMpJywgbGluZSA9IGxpc3QoY29sb3IgPSAnZ3JlZW4nLCBkYXNoID0gJ2Rhc2gnKSkgJT4lCiAgbGF5b3V0KHRpdGxlID0gIkV4ZWN1dGlvbiBUaW1lIiwKICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gImxvZyhuKSIsIHRpY2ttb2RlID0gImFycmF5IiwgdGlja3ZhbHMgPSAwOjYsIHRpY2t0ZXh0ID0gYXMuY2hhcmFjdGVyKDA6NikpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiVGltZSAocykiKSwKICAgICAgICAgbGVnZW5kID0gbGlzdCh0aXRsZSA9ICJMZWdlbmQiLCBvcmllbnRhdGlvbiA9ICJoIiwgeCA9IDAuMywgeSA9IC0wLjIpKQpwbG90X2x5KCkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKHNxcnQobiksIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ21hdHJpeF9tdWx0aXBsaWNhdGlvbiBSJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIGRhc2ggPSAnc29saWQnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJibHVlIiwgc3ltYm9sID0gJ2NpcmNsZScpKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IFJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSwgeCA9IH5sb2coc3FydChuKSwgMTApLCB5ID0gfk1lbW9yeSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnbWF0cml4X2ludmVyc2lvbiBSJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIGRhc2ggPSAnZGFzaGRvdCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGFkZF90cmFjZShkYXRhID0gcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSwgeCA9IH5sb2coc3FydChuKSwgMTApLCB5ID0gfk1lbW9yeSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnbWF0cml4X211bHRpcGxpY2F0aW9uIFB5dGhvbicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIsIGRhc2ggPSAnc29saWQnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJyZWQiLCBzeW1ib2wgPSAnZG90JykpICU+JQogIGFkZF90cmFjZShkYXRhID0gcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSwgeCA9IH5sb2coc3FydChuKSwgMTApLCB5ID0gfk1lbW9yeSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnbWF0cml4X2ludmVyc2lvbiBQeXRob24nLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCBkYXNoID0gJ2Rhc2hkb3QnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJyZWQiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGxheW91dCh0aXRsZSA9ICJNZW1vcnkgdXNhZ2UiLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAibG9nKG4pIiwgdGlja21vZGUgPSAiYXJyYXkiLCB0aWNrdmFscyA9IDA6NiwgdGlja3RleHQgPSBhcy5jaGFyYWN0ZXIoMDo2KSksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJNZW1vcnkgKE1pQikiKSwKICAgICAgICAgbGVnZW5kID0gbGlzdCh0aXRsZSA9ICJMZWdlbmQiLCBvcmllbnRhdGlvbiA9ICJoIiwgeCA9IDAuMywgeSA9IC0wLjIpKQpgYGAKCgpJbnRlcmVzdGluZ2x5LCB0aGUgaW1wbGVtZW50YXRpb24gb2YgbWF0cml4IG11bHRpcGxpY2F0aW9uIGFuZCBpbnZlcnNpb24gaW4gUHl0aG9uIHNlZW1zIHRvIHNjYWxlIGxpbmVhcmx5IG9yIGV2ZW4gYmV0dGVyIHdpdGggJG4kLCBidXQgaXQgYmxvd3MgdXAgdG8gb3JkZXIgJE8obl4zKSQgZm9yIFIgZm9yIGxhcmdlciBtYXRyaWNlcy4gRm9yIHNtYWxsZXIgbWF0cmljZXMgb2Ygc2l6ZSBsZXNzIHRoYW4gJDEwMCBcdGltZXMgMTAwJCwgUiBzZWVtcyB0byBiZSBmYXN0ZXIgd2hpbGUgUHl0aG9uIGlzIG11Y2ggZmFzdGVyIGZvciBsYXJnZSAkbj0xMDAwJC4gQW4gaW1wb3J0YW50IG5vdGUgaXMgdGhhdCBtZW1vcnkgYWxzbyBzZWVtcyB0byBpbmNyZWFzZSBsaW5lYXJseSBmb3IgbGFyZ2VyIG1hdHJpY2VzIGluIFB5dGhvbiB3aGljaCBtYXkgYWxzbyBleHBsYWluIHdoeSB0aGUgZXhlY3V0aW9uIHRpbWUgaXMgbXVjaCBiZXR0ZXIgdGhhbiB0aGUgdGhlb3JldGljYWwgY29tcGxleGl0eS4KCgojIyBMaW5lYXIgcmVncmVzc2lvbgoKVGhlIG9yZGluYXJ5IGxlYXN0IHNxdWFyZXMgZXN0aW1hdGUgb2YgbGluZWFyIHJlZ3Jlc3Npb24gJFxoYXR7XGJldGF9JCBmb3IgJFxiZXRhJCBpcyB0aGUgdmVjdG9yIHRoYXQgc29sdmVzIHRoZSBjb250cmFpbnN0ICQkXGFyZyBcbWluX1xiZXRhICh5IC0gWCBcYmV0YV5UKV4yLiQkIElmIHRoZSBkYXRhIGlzIE5vcm1hbGx5IGRpc3RyaWJ1dGVkIGFzIGluIHRoZSBbc2ltdWxhdGlvbl0oI3NldHVwKSwgdGhlbiBpdCBpcyB3ZWxsLWtub3duIHRoYXQgJFxoYXR7XGJldGF9JCBpcyBnaXZlbiBieSAKJCRcaGF0e1xiZXRhfSA9IChYXlQgWCleey0xfSBYXlQgeS4kJApUaGVyZWZvcmUsIE9MUyBjb25zaXN0cyBzaW1wbHkgb2YgbWF0cml4IG11bHRpcGxpY2F0aW9uIGFuZCBpbnZlcnNpb24uIEluIHBzZXVkb2NvZGUsIGl0IGNhbiBiZSBpbXBsZW1lbnRlZCBpbiB0aGUgZm9sbG93aW5nIG1hbm5lci4KCgpgYGBwbGFpbnRleHQKRnVuY3Rpb24gbGluZWFyX3JlZ3Jlc3Npb24oWCwgeSkKICAgIC8vIERpbWVuc2lvbnMKICAgIG4gPC0gbGVuZ3RoIG9mIGRhdGEgeQogICAgcCA8LSBudW1iZXIgb2YgY29sdW1ucyBpbiBYCiAgICBYX2IgPC0gQXBwZW5kIGNvbHVtbiAxIHRvIFggLy8gSW5jbHVkZSBhbiBpbnRlcmNlcHQKICAgIAogICAgLy8gQ29tcHV0ZSBjb2VmZmljaWVudHMKICAgIGJldGFfaGF0IDwtIChYX2IuVCBAIFhfYileLTEgQCAoWF9iLlQgQCB5KSAKICAgIAogICAgUmV0dXJuIGJldGFfaGF0CkVuZCBGdW5jdGlvbgpgYGAKClRoZSBtYXRyaXggYXVnbWVudGF0aW9uIG9wZXJhdGlvbiBpcyAkTyhuKSQgc2luY2Ugd2UgYXJlIGFwcGVuZGluZyAkbiQgcm93cy4gVGhlIG1hdHJpeCBtdWx0aXBsaWNhdGlvbiBhbmQgaW52ZXJzaW9uIGFyZSB0aGUgZG9taW5hdGluZyBzdGVwcyBvZiB0aGlzIGFsZ29yaXRobS4gQXMgZXhwbGFpbmVkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uIG9uIG1hdHJpY2VzLCB0aGUgbWF0cml4IG11bHRpcGxpY2F0aW9uIFhfYi5UIEAgWF9iIHN0ZXAgaXMgJE8obiBcdGltZXMgKHArMSleMikkIHdoaWxlIHRoZSBtYXRyaXggaW52ZXJzaW9uIGlzICRPKChwKzEpXjMpJCwgYWx0aG91Z2ggdGhlc2UgbWF0cml4IG9wZXJhdGlvbnMgYXJlIGxpa2VseSBpbXBsZW1lbnRlZCBtb3JlIGVmZmljaWVudGx5IGluIFB5dGhvbiBhbmQgUi4gVGhlIGxhc3Qgc3RlcCBvZiBtdWx0aXBseWluZyB0aGUgbWF0cml4IChYX2IuVCBAIFhfYileLTEgd2l0aCB0aGUgdmVjdG9yIChYX2IuVCBAIHkpIGlzICRPKChwKzEpXjIpJC4gVGhlcmVmb3JlLCB0aGUgb3ZlcmFsbCBjb21wbGV4aXR5IGlzICRPKHBeMykkIG9yICRPKG4gXHRpbWVzIChwKzEpXjIpJCBkZXBlbmRpbmcgb24gd2hldGhlciAkcDw8biQuIFdlIGNhbiBuZXZlciBoYXZlICRwPm4kIGluIGxpbmVhciByZWdyZXNzaW9uIHNpbmNlIHRoZSBjb2VmZmljaWVudHMgd291bGQgYmVjb21lIHVuaWRlbnRpZmlhYmxlLiBJbiB0aGlzIGNhc2Ugc2luY2Ugd2Uga2VlcCAkcD0xMCQgYW5kIHdlIGluY3JlYXNlICRuJCB0byAkbj0xMF42JCwgdGhlIGRvbWluYXRpbmcgc3RlcCBpcyB0aGUgZmlyc3QgbWF0cml4IG11bHRpcGxpY2F0aW9uIHN0ZXAgd2hpY2ggaXMgJE8obikkLiBUaGlzIHNldHVwIGlzIHR5cGljYWwgb2YgY2xhc3NpY2FsIHJlZ3Jlc3Npb24gd2hlcmUgYSByZWxhdGl2ZWx5IHNtYWxsIG51bWJlciBvZiBjb3ZhcmlhdGVzIGlzIHVzZWQsIGJ1dCBpbiBhIGhpZ2gtZGltZW5zaW9uYWwgc2V0dGluZyB3ZSBjb3VsZCBoYXZlIGxhcmdlICRwJCB3aGljaCB3b3VsZCBtYWtlIHRoZSBtYXRyaXggaW52ZXJzaW9uIHN0ZXAgbW9yZSBjb3N0bHkuCgpJbiBQeXRob24gdGhpcyBjYW4gYmUgaW1wbGVtZW50ZWQgbGlrZSB0aGUgYWxnb3JpdGhtIGFib3ZlIG9yIHVzaW5nIHRoZSBTY2lraXQtTGVhcm4gcGFja2FnZToKYGBge3B5dGhvbiwgZXZhbCA9IEZBTFNFfQojIExpbmVhciByZWdyZXNzaW9uIGZyb20gYmFzZQpkZWYgbGluX3JlZ19iYXNlKFgsIHkpOgogICAgWF9iID0gbnAuaHN0YWNrKFtucC5vbmVzKChYLnNoYXBlWzBdLCAxKSksIFhdKQogICAgYmV0YV9oYXQgPSBucC5saW5hbGcuaW52KFhfYi5UIEAgWF9iKSBAIChYX2IuVCBAIHkpCiAgICByZXR1cm4gYmV0YV9oYXQKICAKIyBza2xlYXJuLUxlYXJuIGxpbmVhciByZWdyZXNzaW9uCmRlZiBsaW5fcmVnX3NrbGVhcm4oWCwgeSk6CiAgICBtb2RlbCA9IExpbmVhclJlZ3Jlc3Npb24oKQogICAgbW9kZWwuZml0KFgsIHkpCiAgICBiZXRhX2hhdCA9IG5wLmhzdGFjayhbbW9kZWwuaW50ZXJjZXB0XywgbW9kZWwuY29lZl9dKQogICAgcmV0dXJuIGJldGFfaGF0CmBgYAoKYW5kIGluIFIgdGhpcyBpcyBhbHNvIGltcGxlbWVudGVkIGluIEJhc2UgUiB1c2luZyB0aGUgbG0oKSBmdW5jdGlvbjoKCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgTGluZWFyIHJlZ3Jlc3Npb24gZnJvbSBiYXNlCmxpbl9yZWdfYmFzZSA8LSBmdW5jdGlvbihYLCB5KSB7CiAgWGIgPC0gY2JpbmQoMSwgWCkKICBiZXRhcyA8LSBzb2x2ZSh0KFhiKSAlKiUgWGIpICUqJSAodChYYikgJSolIHkpCiAgcmV0dXJuKGJldGFzKQp9CgojIExpbmVhciByZWdyZXNzaW9uIHVzaW5nIGxtKCkKbGluX3JlZ19wYWNrYWdlIDwtIGZ1bmN0aW9uKFgsIHkpIHsKICBtb2RlbCA8LSBsbSh5IH4gWCArIDApCiAgcmV0dXJuKGNvZWYobW9kZWwpKQp9CmBgYAoKCgpgYGB7ciBlY2hvPUZBTFNFfQphbGdvcyA8LSBjKCJsaW5lYXJfcmVncmVzc2lvbl9iYXNlIiwgImxpbmVhcl9yZWdyZXNzaW9uX3BhY2thZ2UiKQpuX3ZhbHVlcyA8LSBzZXEoMiwgNiwgYnkgPSAwLjEpCk9fbiA8LSAxMF5uX3ZhbHVlcy8xMF40Ck9fbl9weXRob25fMSA8LSBPX24qcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSRUaW1lWzNdCk9fbl9SXzEgPC0gT19uKlJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSRUaW1lWzNdCk9fbl9weXRob25fMiA8LSBPX24qcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSRUaW1lWzNdCk9fbl9SXzIgPC0gT19uKlJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSRUaW1lWzNdCnBsb3RfbHkoKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IFJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSwgeCA9IH5sb2cobiwgMTApLCB5ID0gflRpbWUsIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ2xpbmVhcl9yZWdyZXNzaW9uX3NjcmF0Y2ggUicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBkYXNoID0gJ3NvbGlkJyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIHN5bWJvbCA9ICdjaXJjbGUnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzJdLF0sIHggPSB+bG9nKG4sIDEwKSwgeSA9IH5UaW1lLCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdsaW5lYXJfcmVncmVzc2lvbl9iYXNlIFInLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibHVlIiwgZGFzaCA9ICdkYXNoZG90JyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIHN5bWJvbCA9ICdzcXVhcmUnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdLCB4ID0gfmxvZyhuLCAxMCksIHkgPSB+VGltZSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnbGluZWFyX3JlZ3Jlc3Npb25fc2NyYXRjaCBQeXRob24nLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCBkYXNoID0gJ3NvbGlkJyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAicmVkIiwgc3ltYm9sID0gJ2RvdCcpKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IHB5dGhvbltweXRob24kQWxnb3JpdGhtID09IGFsZ29zWzJdLF0sIHggPSB+bG9nKG4sIDEwKSwgeSA9IH5UaW1lLCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdsaW5lYXJfcmVncmVzc2lvbl9za2xlYXJuIFB5dGhvbicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIsIGRhc2ggPSAnZGFzaGRvdCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gInJlZCIsIHN5bWJvbCA9ICdzcXVhcmUnKSkgJT4lCiAgYWRkX3RyYWNlKHggPSBuX3ZhbHVlcywgeSA9IE9fbl9SXzEsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLAogICAgICAgICAgICBuYW1lID0gJ08obiknLCBsaW5lID0gbGlzdChjb2xvciA9ICdibGFjaycsIGRhc2ggPSAnZGFzaCcpKSAlPiUKICBsYXlvdXQodGl0bGUgPSAiRXhlY3V0aW9uIFRpbWUiLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAibG9nKG4pIiwgdGlja21vZGUgPSAiYXJyYXkiLCB0aWNrdmFscyA9IDA6NiwgdGlja3RleHQgPSBhcy5jaGFyYWN0ZXIoMDo2KSksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJUaW1lIChzKSIpLAogICAgICAgICBsZWdlbmQgPSBsaXN0KHRpdGxlID0gIkxlZ2VuZCIsIG9yaWVudGF0aW9uID0gImgiLCB4ID0gMC4zLCB5ID0gLTAuMikpCm5fdmFsdWVzIDwtIHNlcSgyLCA2LCBieSA9IDAuMSkKT19uIDwtIDEwXm5fdmFsdWVzLzEwXjQKT19uX3B5dGhvbl8xIDwtIE9fbipweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdJE1lbW9yeVszXQpPX25fUl8xIDwtIE9fbipSW1IkQWxnb3JpdGhtID09IGFsZ29zWzFdLF0kTWVtb3J5WzNdCk9fbl9weXRob25fMiA8LSBPX24qcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSRNZW1vcnlbM10KT19uX1JfMiA8LSBPX24qUltSJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdJE1lbW9yeVszXQpwbG90X2x5KCkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKG4sIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ2xpbmVhcl9yZWdyZXNzaW9uX3NjcmF0Y2ggUicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBkYXNoID0gJ3NvbGlkJyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIHN5bWJvbCA9ICdjaXJjbGUnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzJdLF0sIHggPSB+bG9nKG4sIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ2xpbmVhcl9yZWdyZXNzaW9uX2Jhc2UgUicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBkYXNoID0gJ2Rhc2hkb3QnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJibHVlIiwgc3ltYm9sID0gJ3NxdWFyZScpKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IHB5dGhvbltweXRob24kQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKG4sIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ2xpbmVhcl9yZWdyZXNzaW9uX3NjcmF0Y2ggUHl0aG9uJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAicmVkIiwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gInJlZCIsIHN5bWJvbCA9ICdkb3QnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdLCB4ID0gfmxvZyhuLCAxMCksIHkgPSB+TWVtb3J5LCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdsaW5lYXJfcmVncmVzc2lvbl9za2xlYXJuIFB5dGhvbicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIsIGRhc2ggPSAnZGFzaGRvdCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gInJlZCIsIHN5bWJvbCA9ICdzcXVhcmUnKSkgJT4lCiAgYWRkX3RyYWNlKHggPSBuX3ZhbHVlcywgeSA9IE9fbl9SXzIsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLAogICAgICAgICAgICBuYW1lID0gJ08obiknLCBsaW5lID0gbGlzdChjb2xvciA9ICdibGFjaycsIGRhc2ggPSAnZGFzaCcpKSAlPiUKICBsYXlvdXQodGl0bGUgPSAiTWVtb3J5IHVzYWdlIiwKICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gImxvZyhuKSIsIHRpY2ttb2RlID0gImFycmF5IiwgdGlja3ZhbHMgPSAwOjYsIHRpY2t0ZXh0ID0gYXMuY2hhcmFjdGVyKDA6NikpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiTWVtb3J5IChNaUIpIiksCiAgICAgICAgIGxlZ2VuZCA9IGxpc3QodGl0bGUgPSAiTGVnZW5kIiwgb3JpZW50YXRpb24gPSAiaCIsIHggPSAwLjMsIHkgPSAtMC4yKSkKYGBgCgoKTGluZWFyIHJlZ3Jlc3Npb24gc2VlbXMgdG8gc2NhbGUgbGluZWFybHkgZm9yIGV4ZWN1dGlvbiB0aW1lIGluIFIsIGJ1dCBleGVjdXRpb24gdGltZSBzZWVtcyBjb25zdGFudCBhbmQgZXZlbiBwZXJwbGV4aW5nbHkgZGVjcmVhc2VzIGZvciBsYXJnZSAkbiQgaW4gUHl0aG9uLiBUaGUgaW1wbGVtZW50YXRpb24gZnJvbSBzY3JhdGNoIGluIGJvdGggUHl0aG9uIGFuZCBSIHdhcyBmYXN0ZXIgdGhhbiB0aGUgaW1wbGVtZW50YXRpb24gZnJvbSBza2xlYXJuIGluIFB5dGhvbiBhbmQgZnJvbSB0aGUgYmFzZSBmdW5jdGlvbiBpbiBSLCBhbmQgUiB3YXMgZmFzdGVyIGZvciBkYXRhIHNldHMgbGVzcyB0aGFuICRuPTEwXjUkIHdoaWxlIFB5dGhvbiBzY2FsZWQgYmV0dGVyIGZvciBsYXJnZSAkbj0xMF42JC4gVGhlIGV4ZWN1dGlvbiB0aW1lIG9mIGxpbmVhciByZWdyZXNzaW9uIGZyb20gc2tsZWFybiBibG93cyB1cCBhdCAkMTBeNiQgYW5kIGlzIGNvbnNpZGVyYWJseSBzbG93ZXIgdGhhbiBhbGwgb3RoZXIgaW1wbGVtZW50YXRpb25zLiBUaGlzIG1ha2VzIHNlbnNlIHNpbmNlIHNjaWtpdC1sZWFybiBhbmQgbG0oKSBjb250YWluIGFkZGl0aW9uYWwgc3RlcHMgdGhhbiBteSBzaW1wbGUgY29kZSwgc3VjaCBhcyB0cmFuc2xhdGluZyB0aGUgZm9ybXVsYSBzeW50YXggaW50byBtYXRyaXggY2FsY3VsYXRpb25zIGFuZCBtYW55IG90aGVyIG9wdGlvbnMgZm9yIG1vcmUgY29tcGxpY2F0ZWQgdmVyc2lvbnMgb2YgbGluZWFyIHJlZ3Jlc3Npb24sIGFsdGhvdWdoIEkgYW0gbm90IHN1cmUgd2h5IGl0IHNjYWxlcyBzbyBwb29ybHkgZm9yIGxhcmdlICRuJC4gSW50ZXJlc3RpbmdseSwgbWVtb3J5IHNjYWxlZCByb3VnaGx5IGxpbmVhcmx5IGFuZCBhbGwgYWxnb3JpdGhtcyBpbiBib3RoIFIgYW5kIFB5dGhvbiBoYWQgc2ltaWxhciBtZW1vcnkgY3VydmVzLCB3aGljaCBtYXkgZXhwbGFpbiB3aHkgZXhlY3V0aW9uIHRpbWUgZGlkIG5vdCBzZWVtIHRvIGluY3JlYXNlIGluIHRoZSBiYXNpYyBpbXBsZW1lbnRpb24gaW4gUHl0aG9uLgoKIyMgQm9vdHN0cmFwCgpUaGUgYm9vdHN0cmFwIGFsZ29yaXRobSBpcyBwb3B1bGFyIGluIHN0YXRpc3RpY3MgYmVjYXVzZSBpdCBhbGxvd3MgZm9yIHRoZSBlc3RpbWF0aW9uIG9mIHRoZSBwcm9wZXJ0aWVzIG9mIGdlbmVyYWwgZXN0aW1hdG9ycyB1bmRlciBtaW5pbWFsIGFzc3VtcHRpb25zIGV2ZW4gaWYgdGhleSBhcmUgbm90IGFuYWx5dGljYWxseSB0cmFjdGFibGUuIFRoZSBpZGVhIG9mIHRoZSBib290c3RyYXAgaXMgdG8gcmVzYW1wbGUgZnJvbSB0aGUgZGF0YSB0byBjcmVhdGUgbmV3IHN5bnRoZXRoaWMgZGF0YXNldHMgYW5kIGNvbXB1dGUgYSBuZXcgZXN0aW1hdG9yIGZvciBlYWNoIGRhdGFzZXQsIGZyb20gd2hpY2ggdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZXN0aW1hdG9yIGNhbiBiZSBlc3RpbWF0ZWQuIFRoZSBib290c3RyYXAgd2FzIGluZmx1ZW50aWFsIGluIG1vdmluZyBzdGF0aXN0aWNzIGF3YXkgZnJvbSBtYXRoZW1hdGljcyBhbmQgdG93YXJkcyBjb21wdXRhdGlvbiBzaW5jZSBvbmUgZG9lcyBub3QgbmVlZCB0byBrbm93IGFueXRoaW5nIGFib3V0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGVzdGltYXRvciB0byBwZXJmb3JtIHRoZSBhbGdvcml0aG0uIFRoZSBtYWpvciBhc3N1bXB0aW9ucyBvZiB0aGUgc3RhbmRhcmQgYm9vdHN0cmFwIGFyZSB0aGF0IHRoZSBkYXRhIGFyZSBpbmRlcGVuZGVudCBhbmQgaWRlbnRpY2FsbHkgZGlzdHJpYnV0ZWQgYW5kIHRoYXQgdGhlIGVtcGlyaWNhbCBjdW11bGF0aXZlIGRlbnNpdHkgZnVuY3Rpb24gKENERikgaXMgY2xvc2UgdG8gdGhlIHRydWUgQ0RGIG9mIHRoZSBkYXRhLiBUaGUgbGF0dGVyIGlzIGFzeW1wdG90aWNhbGx5IGp1c3RpZmllZCBidXQgZW1waXJpY2FsbHkgdW50ZXN0YWJsZS4KCkhlcmUgaXMgYSBwc2V1ZG9jb2RlIGZvciB0aGUgYm9vdHN0cmFwIGFsZ29yaXRobS4KCmBgYHBsYWludGV4dApGdW5jdGlvbiBib290c3RyYXAoZGF0YSwgc3RhdGlzdGljLCBCLCBhbHBoYSkKICAgIG4gPC0gbGVuZ3RoIG9mIGRhdGEKICAgIGlkeCA8LSBzYW1wbGUgQiBzZXRzIG9mIGluZGljZXMgd2l0aCByZXBsYWNlbWVudCAKICAgIHNhbXBsZXMgPC0gZXh0cmFjdCBkYXRhIGVsZW1lbnRzIGJhc2VkIG9uIGlkeAogICAgc3RhdCA8LSBzb3J0KHN0YXRpc3RpYyhzYW1wbGVzKSkKICAgIGxvd2VyX0NJIDwtIGV4dHJhY3QgKDEwMCooMS1hbHBoYSkvMil0aCBzYW1wbGUKICAgIHVwcGVyX0NJIDwtIGV4dHJhY3QgKDEwMCooMS0oMS1hbHBoYSkvMil0aCBzYW1wbGUKICAgIFJldHVybiBsb3dlcl9DSSwgdXBwZXJfQ0kKRW5kIEZ1bmN0aW9uCmBgYAoKVGhlIGlkeCByZXNhbXBsaW5nIHN0ZXAgaXMgJE8obiBcdGltZXMgQikkIGFuZCBleHRyYWN0aW5nIHRoZSBzYW1wbGVzIGlzIGFsc28gJE8obiBcdGltZXMgQikkLiBGb3IgYSBsaW5lYXIgc3RhdGlzdGljIGxpa2UgYSBzYW1wbGUgbWVhbiwgdGhlIHN0YXRpc3RpYyBzdGVwIGlzIGFsc28gJE8obiBcdGltZXMgQikkLiBUaGUgc29ydGluZyBzdGVwIGRlcGVuZHMgb24gdGhlIGFsZ29yaXRobSB1c2VkLCBidXQgdGhlIG9uZSB1c2VkIGluIFB5dGhvbiBpcyBUaW1zb3J0IHdoaWNoIGhhcyAkTyhCIFxsb2cgKEIpKSQuIEFmdGVyIHRoYXQsIGNvbXB1dGluZyB0aGUgdHdvIHNhbXBsZSBxdWFudGlsZXMgb24gdGhlIHNvcnRlZCBkYXRhIGlzICRPKDEpJC4gSG93ZXZlciwgYSBwb3RlbnRpYWxseSBtb3JlIGVmZmljaWVudCB3YXkgb2YgZG9pbmcgdGhpcyBzdGVwIGlzIHVzaW5nIHNhbXBsZSBxdWFudGlsZXMgc2luY2Ugd2UgYXJlIG9ubHkgaW50ZXJlc3RlZCBpbiB0d28gc2FtcGxlIHF1YW50aWxlcyB3aGljaCBtYXkgYXZvaWQgc29ydGluZyB0aGUgZW50aXJlIGRhdGFzZXQuIFRoZXJlZm9yZSwgc2luY2Ugd2UgaW5jcmVhc2UgJEIkIGFuZCBrZWVwICRuPTEwXjMkIGZpeGVkLCB0aGUgZG9taW5hdGluZyBzdGVwIGlzIGxpa2VseSB0aGUgcmVzYW1wbGluZy9leHRyYWN0aW5nL3N0YXRpc3RpYyBzdGVwLCB3aGljaCBoYXMgY29tcGxleGl0eSAkTyhCKSQsIGFsdGhvdWdoIGlmIHdlIHVzZSBhIG5haXZlIHNvcnRpbmcgYWxnb3JpdGhtIGxpa2UgYnViYmxlIHNvcnQgdGhpcyBtYXkgYmVjb21lIGNvc3RseS4gVGhlcmUgYXJlIG90aGVyIG1vcmUgY29tcGxpY2F0ZWQgaW1wbGVtZW50YXRpb25zIG9mIHRoZSBib290c3RyYXAgdGhhdCBkbyBub3QgdXNlIHNhbXBsZSBxdWFudGlsZXMgZm9yIHRoZSBjb25maWRlbmNlIGludGVydmFsLiBBIHBvcHVsYXIgYXBwcm9hY2ggaXMgdG8gdXNlIGEgYm9vdHN0cmFwcGVkIHBpdm90IHdpdGhpbiBlYWNoIGl0ZXJhdGlvbiBvZiB0aGUgYWxnb3JpdGhtLCB0aGVyZWJ5IG5lc3RpbmcgYW4gYWRkaXRpb25hbCBsb29wIGluIHRoZSBhbGdvcml0aG0gd2hpY2ggaW5jcmVhc2VzIHRoZSBjb21wbGV4aXR5IHRvICRPKEJeMikkLgoKCkluIFB5dGhvbiB0aGUgc3RhbmRhcmQgYm9vdHN0cmFwIGNhbiBiZSBpbXBsZW1lbnRlZCB1c2luZyB0aGUgYWxnb3JpdGhtIGFib3ZlIHdpdGggdmVjdG9yaXplZCBvcGVyYXRpb25zIGZvciBlZmZpY2llbmN5LCBvciB1c2luZyB0aGUgc2NpcHkuc3RhdHMgbGlicmFyeSBhcyBmb2xsb3dzLgoKYGBge3B5dGhvbiwgZXZhbCA9IEZBTFNFfQojIEJvb3RzdHJhcCBmcm9tIGJhc2UKZGVmIGJvb3RzdHJhcF9iYXNlKGRhdGEsIHN0YXRpc3RpYywgQik6CiAgICBuID0gbGVuKGRhdGEpCiAgICBpZHggPSBucC5yYW5kb20ucmFuZGludCgwLCBuLCAoQiwgbikpCiAgICBzYW1wbGVzID0gZGF0YVtpZHhdCiAgICBzdGF0ID0gc3RhdGlzdGljKHNhbXBsZXMpCiAgICBjb25maWRlbmNlX2ludGVydmFsID0gbnAucXVhbnRpbGUoc3RhdCwgWzAuMDI1LCAwLjk3NV0pCiAgICByZXR1cm4gY29uZmlkZW5jZV9pbnRlcnZhbAogIAojIHNjaXB5LnN0YXRzIGJvb3RzdHJhcApkZWYgYm9vdHN0cmFwX3NjaXB5KGRhdGEsIHN0YXRpc3RpYywgQiwgY29uZmlkZW5jZV9sZXZlbD0wLjk1KToKICAgIHJlcyA9IHN0YXRzLmJvb3RzdHJhcCgoZGF0YSwpLCBzdGF0aXN0aWMsIG5fcmVzYW1wbGVzPUIsIGNvbmZpZGVuY2VfbGV2ZWw9Y29uZmlkZW5jZV9sZXZlbCwgbWV0aG9kPSdwZXJjZW50aWxlJykKICAgIHJldHVybiByZXMuY29uZmlkZW5jZV9pbnRlcnZhbAogIApkZWYgc2FtcGxlX21lYW4oZGF0YSk6CiAgICByZXR1cm4gc3VtKGRhdGEpL2xlbihkYXRhKQpgYGAKCkluIFIgdGhpcyBjYW4gYWxzbyBiZSBpbXBsZW1lbnRlZCB1c2luZyBhIHZlY3Rvcml6ZWQgdmVyc2lvbiBvciB1c2luZyB0aGUgYm9vdCBwYWNrYWdlLgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KIyBCb290c3RyYXAgZnJvbSBiYXNlCmJvb3RzdHJhcF9iYXNlIDwtIGZ1bmN0aW9uKGRhdGEsIHN0YXRpc3RpYywgQikgewogIG4gPC0gbGVuZ3RoKGRhdGEpCiAgcmVzdWx0cyA8LSByZXBsaWNhdGUoQiwgc3RhdGlzdGljKHNhbXBsZShkYXRhLCBzaXplID0gbGVuZ3RoKGRhdGEpLCByZXBsYWNlID0gVFJVRSkpKQogIGNpIDwtIHF1YW50aWxlKHJlc3VsdHMsIHByb2JzID0gYygwLjAyNSwgMC45NzUpKQogIHJldHVybihjaSkKfQptZWFuX2Z1bmN0aW9uIDwtIGZ1bmN0aW9uKGRhdGEpIHsKICBtZWFuKGRhdGEpCn0KCiMgQm9vdHN0cmFwIGZyb20gYm9vdApib290c3RyYXBfcGFja2FnZSA8LSBmdW5jdGlvbihkYXRhLCBzdGF0aXN0aWMsIFIpIHsKICByZXN1bHQgPC0gYm9vdChkYXRhLCBzdGF0aXN0aWMgPSBzdGF0aXN0aWMsIFIgPSBSKQogIGNpIDwtIGJvb3QuY2kocmVzdWx0LCB0eXBlID0gInBlcmMiKSRwZXJjZW50WzQ6NV0KICByZXR1cm4oY2kpCn0KCm1lYW5fYm9vdCA8LSBmdW5jdGlvbihkYXRhLCBpbmRpY2VzKSB7CiAgbWVhbihkYXRhW2luZGljZXNdKQp9CmBgYAoKCgpgYGB7ciBlY2hvPUZBTFNFfQphbGdvcyA8LSBjKCJib290c3RyYXBfYmFzZSIsICJib290c3RyYXBfcGFja2FnZSIpCm5fdmFsdWVzIDwtIHNlcSgxLCA1LCBieSA9IDAuMSkKT19uIDwtIDEwXm5fdmFsdWVzLzEwXjMKT19uX3B5dGhvbl8xIDwtIE9fbipweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdJFRpbWVbM10KT19uX1JfMSA8LSBPX24qUltSJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdJFRpbWVbM10KT19uX3B5dGhvbl8yIDwtIE9fbipweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdJFRpbWVbM10KT19uX1JfMiA8LSBPX24qUltSJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdJFRpbWVbM10KcGxvdF9seSgpICU+JQogIGFkZF90cmFjZShkYXRhID0gUltSJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdLCB4ID0gfmxvZyhuLzEwLCAxMCksIHkgPSB+VGltZSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnYm9vdHN0cmFwX3NjcmF0Y2ggUicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBkYXNoID0gJ3NvbGlkJyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIHN5bWJvbCA9ICdjaXJjbGUnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzJdLF0sIHggPSB+bG9nKG4vMTAsIDEwKSwgeSA9IH5UaW1lLCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdib290c3RyYXBfYm9vdCBSJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIGRhc2ggPSAnZGFzaGRvdCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGFkZF90cmFjZShkYXRhID0gcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSwgeCA9IH5sb2cobi8xMCwgMTApLCB5ID0gflRpbWUsIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ2Jvb3RzdHJhcF9zY3JhdGNoIFB5dGhvbicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIsIGRhc2ggPSAnc29saWQnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJyZWQiLCBzeW1ib2wgPSAnZG90JykpICU+JQogIGFkZF90cmFjZShkYXRhID0gcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSwgeCA9IH5sb2cobi8xMCwgMTApLCB5ID0gflRpbWUsIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ2Jvb3RzdHJhcF9zY2lweSBQeXRob24nLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCBkYXNoID0gJ2Rhc2hkb3QnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJyZWQiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGFkZF90cmFjZSh4ID0gbl92YWx1ZXMsIHkgPSBPX25fUl8xLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywKICAgICAgICAgICAgbmFtZSA9ICdPKG4pJywgbGluZSA9IGxpc3QoY29sb3IgPSAnYmxhY2snLCBkYXNoID0gJ2Rhc2gnKSkgJT4lCiAgbGF5b3V0KHRpdGxlID0gIkV4ZWN1dGlvbiBUaW1lIiwKICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gImxvZyhCKSIsIHRpY2ttb2RlID0gImFycmF5IiwgdGlja3ZhbHMgPSAwOjYsIHRpY2t0ZXh0ID0gYXMuY2hhcmFjdGVyKDA6NikpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiVGltZSAocykiKSwKICAgICAgICAgbGVnZW5kID0gbGlzdCh0aXRsZSA9ICJMZWdlbmQiLCBvcmllbnRhdGlvbiA9ICJoIiwgeCA9IDAuMywgeSA9IC0wLjIpKQpPX24gPC0gMTBebl92YWx1ZXMvMTBeMwpPX25fcHl0aG9uXzEgPC0gT19uKnB5dGhvbltweXRob24kQWxnb3JpdGhtID09IGFsZ29zWzFdLF0kTWVtb3J5WzNdCk9fbl9SXzEgPC0gT19uKlJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSRNZW1vcnlbM10KT19uX3B5dGhvbl8yIDwtIE9fbipweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdJE1lbW9yeVszXQpPX25fUl8yIDwtIE9fbipSW1IkQWxnb3JpdGhtID09IGFsZ29zWzJdLF0kTWVtb3J5WzNdCnBsb3RfbHkoKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IFJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSwgeCA9IH5sb2cobi8xMCwgMTApLCB5ID0gfk1lbW9yeSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnYm9vdHN0cmFwX3NjcmF0Y2ggUicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBkYXNoID0gJ3NvbGlkJyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIHN5bWJvbCA9ICdjaXJjbGUnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzJdLF0sIHggPSB+bG9nKG4vMTAsIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ2Jvb3RzdHJhcF9ib290IFInLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibHVlIiwgZGFzaCA9ICdkYXNoZG90JyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIHN5bWJvbCA9ICdzcXVhcmUnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdLCB4ID0gfmxvZyhuLzEwLCAxMCksIHkgPSB+TWVtb3J5LCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdib290c3RyYXBfc2NyYXRjaCBQeXRob24nLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCBkYXNoID0gJ3NvbGlkJyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAicmVkIiwgc3ltYm9sID0gJ2RvdCcpKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IHB5dGhvbltweXRob24kQWxnb3JpdGhtID09IGFsZ29zWzJdLF0sIHggPSB+bG9nKG4vMTAsIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ2Jvb3RzdHJhcF9zY2lweSBQeXRob24nLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCBkYXNoID0gJ2Rhc2hkb3QnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJyZWQiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGxheW91dCh0aXRsZSA9ICJNZW1vcnkgdXNhZ2UiLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAibG9nKEIpIiwgdGlja21vZGUgPSAiYXJyYXkiLCB0aWNrdmFscyA9IDA6NiwgdGlja3RleHQgPSBhcy5jaGFyYWN0ZXIoMDo2KSksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJNZW1vcnkgKE1pQikiKSwKICAgICAgICAgbGVnZW5kID0gbGlzdCh0aXRsZSA9ICJMZWdlbmQiLCBvcmllbnRhdGlvbiA9ICJoIiwgeCA9IDAuMywgeSA9IC0wLjIpKQpgYGAKCgpJbiB0aGUgcGxvdHMgYWJvdmUsIHdlIHNlZSB0aGF0IHRoZSBleGVjdXRpb24gdGltZSBmb3IgYm90aCB0aGUgcGFja2FnZSBhbmQgYmFzZSBpbXBsZW1lbnRhdGlvbnMgb2YgdGhlIGJvb3RzdHJhcCBpbiBQeXRob24gYmxvdyB1cCBmb3IgbGFyZ2UgJEI9MTBeNSQuIFNpbmNlIHRoZSBwbG90cyBhcmUgaW50ZXJhY3RpdmUsIEkgZW5jb3VyYWdlIHlvdSB0byByZW1vdmUgdGhlIFB5dGhvbiBkYXRhIHBvaW50cyBieSBjbGlraW5nIG9uIHRoZW0gaW4gdGhlIGxlZ2VuZC4gVGhlbiB3ZSBjYW4gc2VlIHRoYXQgdGhlIGltcGxlbWVudGF0aW9ucyBpbiBSIHNjYWxlIGxpbmVhcmx5IGFzIGV4cGVjdGVkIGZyb20gdGhlIHRoZW9yZXRpY2FsIGNvbXBsZXhpdHkuIFdlIGFsc28gc2VlIHRoYXQgbWVtb3J5IGluY3JlYXNlcyBleHRyZW1lbHkgZmFzdCBpbiBib3RoIFIgYW5kIFB5dGhvbiwgcmVhY2hpbmcgNEdCIGZvciAkQj0xMF41JCBpbiBQeXRob24gYW5kIDJHQiBpbiBSLiBSdW5uaW5nICQxMF41JCBpdGVyYXRpb25zIGlzIGxpa2VseSBvdmVya2lsbCBpbiBwcmFjdGljZSwgcGFydGljdWxhcmx5IGZvciBhIHNhbXBsZSBtZWFuIHdoaWNoIGhhcyByYXBpZCBjb252ZXJnZW5jZSBieSB0aGUgbGF3IG9mIGxhcmdlIG51bWJlcnMuIEluIGdlbmVyYWwsIHRoZSBNb250ZSBDYXJsbyBlcnJvciBmb3IgYSBzaW1wbGUgZXN0aW1hdG9yIG9mdGVuIGJlY29tZXMgbXVjaCBzbWFsbGVyIHRoZW4gdGhlIHNhbXBsaW5nIHZhcmlhbmNlIGFib3ZlICQxMF4zJCBvciAkMTBeNCQgaXRlcmF0aW9ucy4KCgojIyBNQ01DCgoKTWFya292IGNoYWluIE1vbnRlIENhcmxvIGFsZ29yaXRobXMgKE1DTUMpIGFyZSBjZW50cmFsIG1ldGhvZHMgdG8gQmF5ZXNpYW4gc3RhdGlzdGljcyBzaW5jZSB0aGV5IGFsbG93IHVzIHRvIGVzdGltYXRlIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIG9mIHBhcmFtZXRlcnMgd2l0aCBnZW5lcmFsIHByaW9ycyBhbmQgbGlrZWxpaG9vZHMuIEJheWVzIHRoZW9yZW0gc2hvd3MgaG93IHRvIG9idGFpbiBhIHBvc3RlcmlvciBkaXN0cmlidXRpb24gYnkgbXVsdGlwbHlpbmcgdGhlIHByaW9yIHdpdGggdGhlIGxpa2VsaWhvb2QgYW5kIGRpdmlkaW5nIGJ5IHRoZSBtYXJnaW5hbCBkaXN0cmlidXRpb24gd2hpY2ggaW52b2x2ZXMgdGFraW5nIGFuIGludGVncmFsLiBJbiBhIGZldyBjYXNlcyBsaWtlIHRoZSBOb3JtYWwgZGlzdHJpYnV0aW9uLCB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBpcyBvZiB0aGUgc2FtZSBmYW1pbHkgYXMgdGhlIHByaW9yIGFuZCBhIGNsb3NlZCBmb3JtIGlzIGF2YWlsYWJsZS4gSG93ZXZlciwgZm9yIGFyYml0cmFyeSBwcmlvcnMgYW5kIGxpa2VsaWhvb2RzIHRoaXMgaW50ZWdyYWwgaXMgdHlwaWNhbGx5IHZlcnkgZGlmZmljdWx0IG9yIGludHJhY3RhYmxlLCBhbmQgbnVtZXJpY2FsIG1ldGhvZHMgZm9yIGNvbXB1dGluZyBpbnRlZ3JhbHMgb24gYSBncmlkIHN1ZmZlciBmcm9tIHRoZSBjdXJzZSBvZiBkaW1lbnNpb25hbGl0eS4gTUNNQyBhbGdvcml0aG1zIHN1Y2ggYXMgdGhlIE1ldHJvcG9saXMtSGFzdGluZ3MgYWxnb3JpdGhtIGFwcHJveGltYXRlcyB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBieSBpdGVyYXRpdmVseSBnZW5lcmF0aW5nIHNhbXBsZXMgZnJvbSBhIHByb3Bvc2FsIGRpc3RyaWJ1dGlvbiBhbmQgY29tcHV0aW5nIGFuIGFjY2VwdGFuY2UgcmF0aW8gdGhhdCBhbGxvd3MgdGhlIHNhbXBsZSB0byBhcHByb2FjaCB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbi4gVGhlIGFjY2VwdGFuY2UgcmF0aW8gZGVwZW5kcyBvbmx5IG9uIHRoZSBjdXJyZW50IGFuZCBwcmV2aW91cyBzYW1wbGUsIGFuZCBpdCBhdm9pZHMgaGF2aW5nIHRvIGNvbXB1dGUgdGhlIG5vcm1hbGl6YXRpb24gY29uc3RhbnQgc2luY2UgaXQgY2FuY2VscyBvdXQgaW4gdGhlIHJhdGlvLiBBIHNpbXBsZSBwcm9wb3NhbCBmdW5jdGlvbiB1c2VzIHRoZSBHYXVzc2lhbiBkaXN0cmlidXRpb24gYXQgdGhlIGN1cnJlbnQgcG9pbnQsIHdoaWxlIG1vcmUgb3RoZXIgTUNNQyBhbGdvcml0aG1zIGxpa2UgSGFtaWx0b25pYW4gTW9udGUgQ2FybG8gdXNlIG1vcmUgY29tcGxpY2F0ZWQgcHJvcG9zYWwgZGlzdHJpYnV0aW9ucyBieSBsZXZlcmFnaW5nIEhhbWlsdG9uaWFuIGR5bmFtaWNzLiBNQ01DIGFsZ29yaXRobXMgYXJlIHdlbGwganVzdGlmaWVkIHRoZW9yZXRpY2FsbHksIGFuZCB1bmxpa2UgdGhlIGJvb3RzdHJhcCBpdCBpcyBwb3NzaWJsZSB0byBhc3Nlc3MgdGhlIGNvbnZlcmdlbmNlIG9mIHRoZSBhbGdvcml0aG0gYnkgcnVubmluZyBtdWx0aXBsZSBjaGFpbnMgYW5kIGV4YW1pbmluZyB3aGV0aGVyIHRoZSBjaGFpbnMgY29udmVyZ2UgdG8gdGhlIHNhbWUgZGlzdHJpYnV0aW9uLCBhbHRob3VnaCBJIGRvIG5vdCBjb25zaWRlciB0aGlzIGhlcmUuCgpIZXJlIGlzIGEgc2ltcGxlIHBzZXVkb2NvZGUgaW1wbGVtZW50YXRpb24gb2YgdGhlIE1ldHJvcG9saXMgSGFzdGluZ3MgYWxnb3JpdGhtIGZvciB0aGUgc2FtZSBsaW5lYXIgcmVncmVzc2lvbiBzZXR0aW5nIGFzIGJlZm9yZS4gTGluZWFyIHJlZ3Jlc3Npb24gaXMgYWN0dWFsbHkgY29uanVnYXRlIHdpdGggTm9ybWFsIGRpc3RyaWJ1dGlvbnMgd2hpY2ggSSB1c2UgaGVyZSBmb3Igc2ltcGxpY2l0eSwgc28gTWV0cm9wb2xpcy1IYXN0aW5ncyBvciBldmVuIGFueSBhcHByb3hpbWF0aW9uIGlzIG5vdCBzdHJpY3RseSBuZWNlc3NhcnkgaW4gdGhpcyBzZXR0aW5nLgoKYGBgcGxhaW50ZXh0CkZ1bmN0aW9uIG1ldHJvcG9saXNfaGFzdGluZ3MoWCwgeSwgQiwgYmV0YV8wLCBwcm9wb3NhbF9zZCwgc2lnbWEpCiAgICAvLyBJbmNsdWRlIGFuIGludGVyY2VwdAogICAgWF9iIDwtIEFwcGVuZCBjb2x1bW4gMSB0byBYCiAgICAKICAgIC8vIEluaXRpYWxpemUgcGFyYW1ldGVycwogICAgY3VycmVudF9iZXRhIDwtIGJldGFfMAogICAgc2FtcGxlcyA8LSBjdXJyZW50X2JldGEKICAgIFhiIDwtIFhfYiBkb3QgY3VycmVudF9iZXRhCiAgICBjdXJyZW50X2xpa2VsaWhvb2QgPC0gc3VtKGxvZyhOKHl8WGIsc2lnbWEpKQogICAgY3VycmVudF9wcmlvciA8LSBzdW0obG9nKE4oY3VycmVudF9iZXRhfG11MCxzaWdtYTApKSkKICAgIAogICAgLy8gU2FtcGxpbmcKICAgIEZvciBpIGZyb20gMSB0byBCCiAgICAgICAgLy8gUHJvcG9zYWwKICAgICAgICBwcm9wb3NlZF9iZXRhIDwtIHNhbXBsZSBOKGN1cnJlbnRfYmV0YSxwcm9wb3NhbF9zZCkKICAgICAgICBYYl9wcm9wb3NlZCA8LSBYX2IgZG90IHByb3Bvc2VkX2JldGEKICAgICAgICBwcm9wb3NlZF9saWtlbGlob29kIDwtIHN1bShsb2coTih5fFhiX3Byb3Bvc2VkLHNpZ21hKSkKICAgICAgICBwcm9wb3NlZF9wcmlvciA8LSBzdW0obG9nKE4ocHJvcG9zZWRfYmV0YXxtdTAsc2lnbWEwKSkpCiAgICAgICAgCiAgICAgICAgLy8gQWNjZXB0L3JlamVjdCBuZXcgYmV0YQogICAgICAgIHBfYWNjZXB0IDwtIGV4cChwcm9wb3NlZF9saWtlbGlob29kICsgcHJvcG9zZWRfcHJpb3IgLSBjdXJyZW50X2xpa2VsaWhvb2QgLSBjdXJyZW50X3ByaW9yKQogICAgICAgIFUgPC0gc2FtcGxlIFVuaWYoMCwxKQogICAgICAgIElmIFUgPCBwX2FjY2VwdAogICAgICAgICAgICBjdXJyZW50X2JldGEgPC0gcHJvcG9zZWRfYmV0YQogICAgICAgICAgICBjdXJyZW50X2xpa2VsaWhvb2QgPC0gcHJvcG9zZWRfbGlrZWxpaG9vZAogICAgICAgICAgICBjdXJyZW50X3ByaW9yIDwtIHByb3Bvc2VkX3ByaW9yCiAgICAgICAgCiAgICAgICAgQXBwZW5kIGN1cnJlbnRfYmV0YSB0byBzYW1wbGVzCiAgICAKICAgIFJldHVybiBzYW1wbGVzCkVuZCBGdW5jdGlvbgpgYGAKCgpBdWdtZW50aW5nIHRoZSBtYXRyaXggaXMgJE8obikkLCB0aGUgbWF0cml4IGRvdCBwcm9kdWN0cyBmcm9tIGxpbmVhciByZWdyZXNzaW9uIGlzICRPKG4gXHRpbWVzIHApJCwgY29tcHV0aW5nIHRoZSBsb2ctbGlrZWxpaG9vZCBpcyAkTyhuKSQgYW5kIGNvbXB1dGluZyB0aGUgcHJpb3IgaXMgJE8ocCkkLiBUaGVyZWZvcmUsIHJ1bm5pbmcgdGhlIGxvb3AgZm9yIEIgaXRlcmF0aW9ucyBoYXMgY29tcGxleGl0eSAkTyhuIFx0aW1lcyBwIFx0aW1lcyBCKSQsIG9yIGtlZXBpbmcgYWxsIGVsc2UgZml4ZWQgaXQgaXMgJE8oQikkLiBUaGlzIHNlZW1zIGdyZWF0IHNpbmNlIHdlIHNlZW0gdG8gaGF2ZSBhdm9pZGVkIGN1cnNlIG9mIGRpbWVuc2lvbmFsaXR5LCBidXQgaXQgaGlkZXMgdGhlIGltcG9ydGFudCBmYWN0IHRoYXQgdGhpcyBhbGdvcml0aG0gbXVzdCBiZSBydW4gdW50aWwgaXQgY29udmVyZ2VzIHRvIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uLiBUaGlzIGlzIGFjdHVhbGx5IGhpZ2hseSBkZXBlbmRlbnQgb24gZGltZW5zaW9uIGJlY2F1c2UgdGhlIHJhbmRvbSB3YWxrIHdpbGwgbmVlZCB0byBydW4gbXVjaCBsb25nZXIgaW4gaGlnaCBkaW1lbnNpb25zIHRvIGV4cGxvcmUgdGhlIGVudGlyZSBzcGFjZSBvZiB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbi4gU29tZSBhbGdvcml0aG1zIGxpa2UgSGFtaWx0b25pYW4gTW9udGUgQ2FybG8gaW1wbGVtZW50ZWQgaW4gU3RhbiBpbiBib3RoIFIgYW5kIFB5dGhvbiBoZWxwIGFkZHJlc3MgdGhpcyBpc3N1ZSB0aG91Z2ggbW9yZSBjb21wbGljYXRlZCBwcm9wb3NhbCBmdW5jdGlvbnMgdGhhdCByZWR1Y2UgdGhlIGF1dG9jb3JyZWxhdGlvbiBiZXR3ZWVuIHByb3Bvc2Fscy4KClRoZSBNZXRyb3BvbGlzLUhhc3RpbmdzIGFsZ29yaXRobSBjYW4gYmUgaW1wbGVtZW50ZWQgaW4gUHl0aG9uIGFzIGZvbGxvd3MuCgpgYGB7cHl0aG9uLCBldmFsID0gRkFMU0V9CiMgTWV0cm9wb2xpc19oYXN0aW5ncyBmcm9tIHNjcmF0Y2gKZGVmIG1ldHJvcG9saXNfaGFzdGluZ3MoWCwgeSwgbnVtX3NhbXBsZXMsIGJldGFfMD1ucC56ZXJvcygxMSksIHByb3Bvc2FsX3NkPTEsIHNpZ21hPTEpOgogICAgWCA9IG5wLmhzdGFjayhbbnAub25lcygoWC5zaGFwZVswXSwgMSkpLCBYXSkKICAgIGN1cnJlbnRfYmV0YSA9IGJldGFfMAogICAgc2FtcGxlcyA9IFtjdXJyZW50X2JldGFdCiAgICAKICAgIFhiID0gWC5kb3QoY3VycmVudF9iZXRhKQogICAgY3VycmVudF9saWtlbGlob29kID0gbnAuc3VtKHN0YXRzLm5vcm0ubG9ncGRmKHksIFhiLCBzaWdtYSkpCiAgICBjdXJyZW50X3ByaW9yID0gbnAuc3VtKHN0YXRzLm5vcm0ubG9ncGRmKGN1cnJlbnRfYmV0YSwgMCwgMTApKQoKICAgIGZvciBpIGluIHJhbmdlKG51bV9zYW1wbGVzKToKICAgICAgICBwcm9wb3NlZF9iZXRhID0gbnAucmFuZG9tLm5vcm1hbChjdXJyZW50X2JldGEsIHByb3Bvc2FsX3NkKQogICAgICAgIFhiX3Byb3Bvc2VkID0gWC5kb3QocHJvcG9zZWRfYmV0YSkKICAgICAgICBwcm9wb3NlZF9saWtlbGlob29kID0gbnAuc3VtKHN0YXRzLm5vcm0ubG9ncGRmKHksIFhiX3Byb3Bvc2VkLCBzaWdtYSkpCiAgICAgICAgcHJvcG9zZWRfcHJpb3IgPSBucC5zdW0oc3RhdHMubm9ybS5sb2dwZGYocHJvcG9zZWRfYmV0YSwgMCwgMTApKQogICAgICAgIAogICAgICAgIHBfYWNjZXB0ID0gbnAuZXhwKChwcm9wb3NlZF9saWtlbGlob29kICsgcHJvcG9zZWRfcHJpb3IpIC0gKGN1cnJlbnRfbGlrZWxpaG9vZCArIGN1cnJlbnRfcHJpb3IpKQogICAgICAgIAogICAgICAgIGlmIG5wLnJhbmRvbS5yYW5kKCkgPCBwX2FjY2VwdDoKICAgICAgICAgICAgY3VycmVudF9iZXRhID0gcHJvcG9zZWRfYmV0YQogICAgICAgICAgICBjdXJyZW50X2xpa2VsaWhvb2QgPSBwcm9wb3NlZF9saWtlbGlob29kCiAgICAgICAgICAgIGN1cnJlbnRfcHJpb3IgPSBwcm9wb3NlZF9wcmlvcgogICAgICAgIAogICAgICAgIHNhbXBsZXMuYXBwZW5kKGN1cnJlbnRfYmV0YSkKICAgIAogICAgcmV0dXJuIG5wLmFycmF5KHNhbXBsZXMpCmBgYAoKVGhlIE1ldHJvcG9saXMtSGFzdGluZ3MgYWxnb3JpdGhtIGNhbiBiZSBpbXBsZW1lbnRlZCBpbiBhIHNpbWlsYXIgZm9ybSBpbiBSIGFzIGZvbGxvd3MuIAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KIyBNZXRyb3BvbGlzX2hhc3RpbmdzIGZvciBsaW5lYXIgcmVncmVzc2lvbiBmcm9tIGJhc2UgUgptZXRyb3BvbGlzX2hhc3RpbmdzIDwtIGZ1bmN0aW9uKFgsIHksIG51bV9zYW1wbGVzLCBiZXRhXzAgPSByZXAoMCwxMSksIHByb3Bvc2FsX3NkPTEsIHNpZ21hPTEpIHsKICBYIDwtIGNiaW5kKDEsIFgpCiAgY3VycmVudF9iZXRhIDwtIGJldGFfMAogIHNhbXBsZXMgPC0gbGlzdChjdXJyZW50X2JldGEpCiAgICAKICBYYiA8LSBYICUqJSBjdXJyZW50X2JldGEKICBjdXJyZW50X2xpa2VsaWhvb2QgPC0gc3VtKGRub3JtKHksIFhiLCBzaWdtYSwgbG9nID0gVFJVRSkpCiAgY3VycmVudF9wcmlvciA8LSBzdW0oZG5vcm0oY3VycmVudF9iZXRhLCAwLCAxMCwgbG9nID0gVFJVRSkpCiAgCiAgZm9yIChpIGluIDE6bnVtX3NhbXBsZXMpIHsKICAgIHByb3Bvc2VkX2JldGEgPC0gbXZybm9ybSgxLCBjdXJyZW50X2JldGEsIGRpYWcocmVwKHByb3Bvc2FsX3NkLCBsZW5ndGgoYmV0YV8wKSkpKQogICAgWGJfcHJvcG9zZWQgPC0gWCAlKiUgcHJvcG9zZWRfYmV0YQogICAgcHJvcG9zZWRfbGlrZWxpaG9vZCA8LSBzdW0oZG5vcm0oeSwgWGJfcHJvcG9zZWQsIHNpZ21hLCBsb2cgPSBUUlVFKSkKICAgIHByb3Bvc2VkX3ByaW9yIDwtIHN1bShkbm9ybShwcm9wb3NlZF9iZXRhLCAwLCAxMCwgbG9nID0gVFJVRSkpCiAgICAKICAgIHBfYWNjZXB0IDwtIGV4cCgocHJvcG9zZWRfbGlrZWxpaG9vZCArIHByb3Bvc2VkX3ByaW9yKSAtIChjdXJyZW50X2xpa2VsaWhvb2QgKyBjdXJyZW50X3ByaW9yKSkKICAgIAogICAgaWYgKHJ1bmlmKDEpIDwgcF9hY2NlcHQpIHsKICAgICAgY3VycmVudF9iZXRhIDwtIHByb3Bvc2VkX2JldGEKICAgICAgY3VycmVudF9saWtlbGlob29kIDwtIHByb3Bvc2VkX2xpa2VsaWhvb2QKICAgICAgY3VycmVudF9wcmlvciA8LSBwcm9wb3NlZF9wcmlvcgogICAgfQogICAgCiAgICBzYW1wbGVzW1tpICsgMV1dIDwtIGN1cnJlbnRfYmV0YQogIH0KICAKICAKICByZXR1cm4oZG8uY2FsbChyYmluZCwgc2FtcGxlcykpCn0KYGBgCgoKQm90aCBSIGFuZCBQeXRob24gYWxzbyBzdXBwb3J0IGludGVyZmFjZXMgZm9yIHRoZSBTdGFuIHByb2dyYW1taW5nIGxhbmd1YWdlIHVzaW5nIGNtZHN0YW5weSBpbiBQeXRob24gYW5kIFJzdGFuIGluIFIuIFN0YW4gcGVyZm9ybXMgZWZmaWNpZW50IEhhbWlsdG9uaWFuIE1vbnRlIENhcmxvIHdpdGggYSBOby1VLVR1cm4gc2FtcGxlci4gVGhlIG5vdGF0aW9uIGluIFN0YW4gaXMgcHJvYmFiaWxpc3RpYyB3aGljaCBpcyBjb252ZW5pZW50IGZvciBzdGF0aXN0aWNhbCBtb2RlbCBidWlsZGluZywgYW5kIHRoZSBjb21wdXRhdGlvbnMgYXJlIGNvbXBpbGVkIGVmZmljaWVudGx5IGluIEMrKy4gSGVyZSBpcyBhbiBleGFtcGxlIG9mIGEgbm9uLWNvbmp1Z2F0ZSBpbXBsZW1lbnRhdGlvbiBvZiBsaW5lYXIgcmVncmVzc2lvbiB3aXRoIGEgQ2F1Y2h5IHByaW9yIG9uIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24uCgpgYGB7c3RhbiBvdXRwdXQudmFyPSJtb2RlbCIsIGV2YWwgPSBGQUxTRX0KZGF0YSB7CmludCBOOyAvLyBTYW1wbGUgc2l6ZQppbnQgSzsgLy8gTnVtYmVyIG9mIHByZWRpY3RvcnMKbWF0cml4W04sIEtdIFg7IC8vIENvdmFyaWF0ZXMKdmVjdG9yW05dIHk7IC8vIE91dGNvbWUKfQpwYXJhbWV0ZXJzIHsKdmVjdG9yW0tdIGJldGE7CnJlYWwgc2lnbWE7Cn0KCm1vZGVsIHsKLy8gUHJpb3JzCmJldGEgfiBub3JtYWwoMCwgMTApOwpzaWdtYSB+IGNhdWNoeSgwLCA1KTsKCi8vIExpa2VsaWhvb2QKeSB+IG5vcm1hbChYICogYmV0YSAsIHNpZ21hKTsKfQpgYGAKCgoKYGBge3IgZWNobz1GQUxTRX0KYWxnb3MgPC0gYygiTWV0cm9wb2xpc19IYXN0aW5ncyIsICJNQ01DX3N0YW4iKQpuX3ZhbHVlcyA8LSBzZXEoMSwgNSwgYnkgPSAwLjEpCk9fbiA8LSAxMF5uX3ZhbHVlcy8xMF4zCk9fbl9weXRob25fMSA8LSBPX24qcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSRUaW1lWzNdCk9fbl9SXzEgPC0gT19uKlJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSRUaW1lWzNdCk9fbl9weXRob25fMiA8LSBPX24qcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSRUaW1lWzNdCk9fbl9SXzIgPC0gT19uKlJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSRUaW1lWzNdCnBsb3RfbHkoKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IFJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSwgeCA9IH5sb2cobi8xMCwgMTApLCB5ID0gflRpbWUsIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ01ldHJvcG9saXNfSGFzdGluZ3MgUicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBkYXNoID0gJ3NvbGlkJyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIHN5bWJvbCA9ICdjaXJjbGUnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzJdLF0sIHggPSB+bG9nKG4vMTAsIDEwKSwgeSA9IH5UaW1lLCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdNQ01DX3N0YW4gUicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBkYXNoID0gJ2Rhc2hkb3QnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJibHVlIiwgc3ltYm9sID0gJ3NxdWFyZScpKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IHB5dGhvbltweXRob24kQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKG4vMTAsIDEwKSwgeSA9IH5UaW1lLCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdNZXRyb3BvbGlzX0hhc3RpbmdzIFB5dGhvbicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIsIGRhc2ggPSAnc29saWQnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJyZWQiLCBzeW1ib2wgPSAnZG90JykpICU+JQogIGFkZF90cmFjZShkYXRhID0gcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSwgeCA9IH5sb2cobi8xMCwgMTApLCB5ID0gflRpbWUsIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ01DTUNfc3RhbiBQeXRob24nLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCBkYXNoID0gJ2Rhc2hkb3QnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJyZWQiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGFkZF90cmFjZSh4ID0gbl92YWx1ZXMsIHkgPSBPX25fUl8xLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywKICAgICAgICAgICAgbmFtZSA9ICdPKG4pJywgbGluZSA9IGxpc3QoY29sb3IgPSAnYmxhY2snLCBkYXNoID0gJ2Rhc2gnKSkgJT4lCiAgYWRkX3RyYWNlKHggPSBuX3ZhbHVlcywgeSA9IE9fbl9weXRob25fMSwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycsCiAgICAgICAgICAgIG5hbWUgPSAnTyhuKScsIGxpbmUgPSBsaXN0KGNvbG9yID0gJ2JsYWNrJywgZGFzaCA9ICdkYXNoJyksIHNob3dsZWdlbmQgPSBGQUxTRSkgJT4lCiAgIyBhZGRfdHJhY2UoeCA9IG5fdmFsdWVzLCB5ID0gT19uX1JfMiwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycsCiAgIyAgICAgICAgICAgbmFtZSA9ICdPKG4pJywgbGluZSA9IGxpc3QoY29sb3IgPSAnYmxhY2snLCBkYXNoID0gJ2Rhc2gnKSwgc2hvd2xlZ2VuZCA9IEZBTFNFKSAlPiUKICAjIGFkZF90cmFjZSh4ID0gbl92YWx1ZXMsIHkgPSBPX25fcHl0aG9uXzIsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLAogICMgICAgICAgICAgIG5hbWUgPSAnTyhuKScsIGxpbmUgPSBsaXN0KGNvbG9yID0gJ2JsYWNrJywgZGFzaCA9ICdkYXNoJyksIHNob3dsZWdlbmQgPSBGQUxTRSkgJT4lCiAgbGF5b3V0KHRpdGxlID0gIkV4ZWN1dGlvbiBUaW1lIiwKICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gImxvZyhCKSIsIHRpY2ttb2RlID0gImFycmF5IiwgdGlja3ZhbHMgPSAwOjYsIHRpY2t0ZXh0ID0gYXMuY2hhcmFjdGVyKDA6NikpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiVGltZSAocykiKSwKICAgICAgICAgbGVnZW5kID0gbGlzdCh0aXRsZSA9ICJMZWdlbmQiLCBvcmllbnRhdGlvbiA9ICJoIiwgeCA9IDAuMywgeSA9IC0wLjIpKQpwbG90X2x5KCkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKG4vMTAsIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ01ldHJvcG9saXNfSGFzdGluZ3MgUicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBkYXNoID0gJ3NvbGlkJyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIHN5bWJvbCA9ICdjaXJjbGUnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzJdLF0sIHggPSB+bG9nKG4vMTAsIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ01DTUNfc3RhbiBSJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIGRhc2ggPSAnZGFzaGRvdCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGFkZF90cmFjZShkYXRhID0gcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSwgeCA9IH5sb2cobi8xMCwgMTApLCB5ID0gfk1lbW9yeSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnTWV0cm9wb2xpc19IYXN0aW5ncyBQeXRob24nLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCBkYXNoID0gJ3NvbGlkJyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAicmVkIiwgc3ltYm9sID0gJ2RvdCcpKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IHB5dGhvbltweXRob24kQWxnb3JpdGhtID09IGFsZ29zWzJdLF0sIHggPSB+bG9nKG4vMTAsIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ01DTUNfc3RhbiBQeXRob24nLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCBkYXNoID0gJ2Rhc2hkb3QnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJyZWQiLCBzeW1ib2wgPSAnc3F1YXJlJykpICU+JQogIGxheW91dCh0aXRsZSA9ICJNZW1vcnkgdXNhZ2UiLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAibG9nKEIpIiwgdGlja21vZGUgPSAiYXJyYXkiLCB0aWNrdmFscyA9IDA6NiwgdGlja3RleHQgPSBhcy5jaGFyYWN0ZXIoMDo2KSksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJNZW1vcnkgKE1pQikiKSwKICAgICAgICAgbGVnZW5kID0gbGlzdCh0aXRsZSA9ICJMZWdlbmQiLCBvcmllbnRhdGlvbiA9ICJoIiwgeCA9IDAuMywgeSA9IC0wLjIpKQpgYGAKCgpCb3RoIFB5dGhvbiBhbmQgUidzIGV4ZWN1dGlvbiB0aW1lcyBmb3IgTWV0cm9wb2xpcy1IYXN0aW5ncyBzZWVtIHRvIHNjYWxlIGxpbmVhcmx5IHdpdGggdGltZSBhcyBleHBlY3RlZCBmcm9tIHRoZSB0aGVvcmV0aWNhbCBjb21wbGV4aXR5LCBhbmQgUiBnZW5lcmFsbHkgc2VlbXMgdG8gYmUgZmFzdGVyIHRoYW4gUHl0aG9uIHBhcnRpY3VsYXJseSBmb3IgYSBsYXJnZSBudW1iZXIgaXRlcmF0aW9ucyBhdCAkQj0xMF41JC4gSW4gdGhpcyBzaW1wbGUgY2FzZSBvZiBsaW5lYXIgcmVncmVzc2lvbiwgcnVubmluZyAkMTBeNSQgaXRlcmF0aW9ucyBpcyBkZWZpbml0ZWx5IHVuY2Vzc2FyeSBhbmQgY29udmVyZ2VuY2Ugd2FzIGFjaGlldmVkIHdheSBiZWZvcmUgYnV0IHRoaXMgbWF5IGJlIG5lZWRlZCBmb3IgdmVyeSBjb21wbGV4IG1vZGVscy4gU3RhbidzIGV4ZWN1dGlvbiB0aW1lIGlzIHZlcnkgc2ltaWxhciBpbiBib3RoIGludGVyZmFjZXMgYW5kIGl0IHNlZW1zIHRvIHNjYWxlIGV2ZW4gYmV0dGVyIHRoYW4gbGluZWFybHkgZm9yIGxhcmdlIG51bWJlciBvZiBpdGVyYXRpb25zLiBTaW5jZSBTdGFuIGhhcyBtdWNoIGxvd2VyIGF1dG9jb3JyZWxhdGlvbiBpbiB0aGUgY2hhaW5zIHRoYW4gTWV0cm9wbGlzLUhhc3RpbmdzLCBTdGFuIGFjdHVhbGx5IGhhcyBtdWNoIGhpZ2hlciBlZmZlY3RpdmUgc2FtcGxlIHNpemUgd2hpY2ggbWFrZXMgaXQgcGVyZm9ybWFuY2UgcXVpdGUgaW1wcmVzc2l2ZSwgYW5kICQxMF41JCBpdGVyYXRpb25zIGlzIHR5cGljYWxseSBtb3JlIHRoYW4gbmVjZXNzYXJ5IGluIFN0YW4uIFdlIGFsc28gc2VlIHRoYXQgbWVtb3J5IHVzYWdlIGJsb3dzIHVwIGluIFIgd2l0aCAzR0Igb2YgbWVtb3J5IHVzZWQgZm9yICQxMF41JCBpdGVyYXRpb25zLCBidXQgaXQgc3RheXMgc3VycHJpc2luZ2x5IGxvdyBiZWxvdyAxMDBNaUIgaW4gUHl0aG9uLgoKCgojIyBTVk0KCkkgd2lsbCBub3Qgc2F5IG11Y2ggYWJvdXQgc3VwcG9ydCB2ZWN0b3IgbWFjaGluZXMgKFNWTSkgYmVjYXVzZSBJIGFtIG5vdCBhcyBmYW1pbGlhciB3aXRoIHRoaXMgYWxnb3JpdGhtIGNvbXBhcmVkIHRvIHN0YXRpc3RpY2FsIG9uZXMuIE5vbmV0aGVsZXNzLCBJIHRob3VnaHQgaXQgd291bGQgYmUgaW50ZXJlc3RpbmcgdG8gaW5jbHVkZSBhIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtIHNpbmNlIHRoaXMgaXMgb2Z0ZW4gdGhvdWdodCB0byBiZSBhIG1ham9yIHN0cmVuZ3RoIG9mIFB5dGhvbiB0aHJvdWdoIGl0cyBsYXJnZSBlY29zeXN0ZW0gb2YgbGlicmFyaWVzLiBNeSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBhbGdvcml0aG0gdXNpbmcgYSBzaW1wbGUgZm9ybSBvZiBncmFkaWVudCBkZXNjZW50IGlzIGFzIGZvbGxvd3MuCgpgYGBwbGFpbnRleHQKRnVuY3Rpb24gc3ZtKFgsIHksIGVwb2NocywgbGVhcm5pbmdfcmF0ZSwgQykKICAgIC8vIEluaXRpYWxpemUgd2VpZ2h0IGFuZCBiaWFzIHBhcmFtZXRlcnMKICAgIHcgPC0gdmVjdG9yIDAKICAgIGIgPC0gMAogICAgCiAgICBGb3IgZWFjaCBlcG9jaCBmcm9tIDEgdG8gZXBvY2hzCiAgICAgICAgRm9yIGVhY2ggc2FtcGxlIGkgZnJvbSAxIHRvIGxlbmd0aCBvZiB5CiAgICAgICAgICAgIERlY2lzaW9uX3ZhbHVlIDwtIFhbaV0gZG90ICh3ICsgYikKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIENoZWNrIGlmIGRhdGEgaXMgb24gdGhlIGNvcnJlY3Qgc2lkZSBvZiB0aGUgbWFyZ2luCiAgICAgICAgICAgIElmIHlbaV0gKiBkZWNpc2lvbl92YWx1ZSA8IDEgdGhlbgogICAgICAgICAgICAgICAgLy8gVXBkYXRlIHcgYW5kIGIgZm9yIGluY29ycmVjdGx5IGNsYXNzaWZpZWQgc2FtcGxlcwogICAgICAgICAgICAgICAgdyA8LSB3ICsgbGVhcm5pbmdfcmF0ZSAqICh5W2ldICogWFtpXSAtIDIgKiAoMS9DKSAqIHcpCiAgICAgICAgICAgICAgICBiIDwtIGIgKyBsZWFybmluZ19yYXRlICogeVtpXQogICAgICAgICAgICBFbHNlCiAgICAgICAgICAgICAgICAvLyBVcGRhdGUgcmVndWxhcml6YXRpb24gZm9yIGNvcnJlY3RseSBjbGFzc2lmaWVkIHNhbXBsZXMKICAgICAgICAgICAgICAgIHcgPC0gdyAtIGxlYXJuaW5nX3JhdGUgKiAoMiAqICgxL0MpICogdykKICAgICAgICAgICAgCiAgICBSZXR1cm4gdywgYgpFbmQgRnVuY3Rpb24KYGBgCgpUaGUgaW5pdGlhbGl6YXRpb25zIGFyZSAkTyhwKSQgYW5kICRPKDEpJCwgdGhlIG91dGVyIGxvb3AgcnVucyBmb3IgZXBvY2hzIGl0ZXJhdGlvbnMgYW5kIHRoZSBpbm5lciBsb29wIHJ1bnMgZm9yICRuJCBpbnRlcmF0aW9ucywgdGhlIHByb2R1Y3QgWFtpXSBkb3QgQyBpcyAkTyhwKSQsIHNvIHRoZSBvdmVyYWxsIGNvbXBsZXhpdHkgaXMgJE8oXHRleHR7ZXBvY2hzfSBcdGltZXMgbiBcdGltZXMgcCkkLiBJbiB0aGlzIGNhc2UsIEkgYW0ga2VlcGluZyBlcG9jaHMgYW5kIHAgZml4ZWQgYW5kIGFtIGluY3JlYXNpbmcgdGhlIHNhbXBsZSBzaXplLCBzbyB0aGUgY29tcGxleGl0eSBpcyAkTyhuKSQuCgpUaGlzIGlzIGltcGxlbWVudGVkIGluIFB5dGhvbiBmb2xsb3dpbmcgdGhlIHBzZXVkb2NvZGUgYW5kIHVzaW5nIHRoZSBza2xlYXJuIGxpYnJhcnkgYXMgZm9sbG93cy4KCmBgYHtweXRob24sIGV2YWwgPSBGQUxTRX0KIyBTVk0gZnJvbSBiYXNlCmRlZiBzdm1fYmFzZShYLCB5LCBlcG9jaHM9MTAwLCBsZWFybmluZ19yYXRlPTAuMDEsIEM9MS4wKToKICAgIHcgPSBucC56ZXJvcyhYLnNoYXBlWzFdKQogICAgYiA9IDAKICAgIGZvciBlcG9jaCBpbiByYW5nZShlcG9jaHMpOgogICAgICAgIGZvciBpIGluIHJhbmdlKGxlbih5KSk6CiAgICAgICAgICAgIGlmIHlbaV0gKiAobnAuZG90KFhbaV0sIHcpICsgYikgPCAxOgogICAgICAgICAgICAgICAgdyArPSBsZWFybmluZ19yYXRlICogKCh5W2ldICogWFtpXSkgKyAoLTIgKiAoMS9DKSAqIHcpKQogICAgICAgICAgICAgICAgYiArPSBsZWFybmluZ19yYXRlICogeVtpXQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgdyAtPSBsZWFybmluZ19yYXRlICogKDIgKiAoMS9DKSAqIHcpCiAgICByZXR1cm4gdywgYgoKIyBza2xlYXJuLUxlYXJuIFNWTQpkZWYgc3ZtX3NrbGVhcm4oWCwgeSk6CiAgICBjbGYgPSBTVkMoa2VybmVsPSdsaW5lYXInLCBDPTEuMCkKICAgIGNsZi5maXQoWCwgeSkKICAgIHJldHVybiBjbGYuY29lZl9bMF0sIGNsZi5pbnRlcmNlcHRfWzBdCmBgYAoKCkxpa2V3aXNlLCB0aGlzIGlzIGltcGxlbWVudGVkIGluIFIgZm9sbG93aW5nIGEgc2ltaWxhciBmb3JtIGFuZCB1c2luZyBhIHBhY2thZ2UgY2FsbGVkIGUxMDcxLgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KIyBTaW1wbGUgbGluZWFyIFNWTSBmcm9tIGJhc2UKc3ZtX2Jhc2UgPC0gZnVuY3Rpb24oWCwgeSwgZXBvY2hzID0gMTAwLCBsZWFybmluZ19yYXRlID0gMC4wMSwgbGFtYmRhID0gMC4wMSkgewogIG5fc2FtcGxlcyA8LSBucm93KFgpCiAgbl9mZWF0dXJlcyA8LSBuY29sKFgpCiAgd2VpZ2h0cyA8LSBtYXRyaXgoMCwgbnJvdyA9IG5fZmVhdHVyZXMsIG5jb2wgPSAxKQogIGludGVyY2VwdCA8LSAwCiAgCiAgZm9yIChlcG9jaCBpbiAxOmVwb2NocykgewogICAgZm9yIChpIGluIDE6bl9zYW1wbGVzKSB7CiAgICAgIGlmICh5W2ldICogKGNyb3NzcHJvZCh3ZWlnaHRzLCBYW2ksIF0pICsgaW50ZXJjZXB0KSA8IDEpIHsKICAgICAgICB3ZWlnaHRzIDwtIHdlaWdodHMgKyBsZWFybmluZ19yYXRlICogKCh5W2ldICogbWF0cml4KFhbaSwgXSwgbmNvbCA9IDEpKSAtICgyICogbGFtYmRhICogd2VpZ2h0cykpCiAgICAgICAgaW50ZXJjZXB0IDwtIGludGVyY2VwdCArIGxlYXJuaW5nX3JhdGUgKiB5W2ldCiAgICAgIH0gZWxzZSB7CiAgICAgICAgd2VpZ2h0cyA8LSB3ZWlnaHRzIC0gbGVhcm5pbmdfcmF0ZSAqICgyICogbGFtYmRhICogd2VpZ2h0cykKICAgICAgfQogICAgfQogIH0KICBsaXN0KGNvZWZmaWNpZW50cyA9IHdlaWdodHMsIGludGVyY2VwdCA9IGludGVyY2VwdCkKfQoKIyBTVk0gdXNpbmcgZTEwNzEgcGFja2FnZQpzdm1fZTEwNzEgPC0gZnVuY3Rpb24oWCwgeSkgewogIG1vZGVsIDwtIHN2bSh4ID0gWCwgeSA9IGFzLmZhY3Rvcih5KSwga2VybmVsID0gImxpbmVhciIpCiAgbGlzdChjb2VmZmljaWVudHMgPSB0KGFzLnZlY3Rvcihtb2RlbCRjb2VmcykgJSolIG1vZGVsJFNWKSwgaW50ZXJjZXB0ID0gLW1vZGVsJHJobykKfQpgYGAKCgoKYGBge3IgZWNobz1GQUxTRX0KYWxnb3MgPC0gYygic3ZtX2Jhc2UiLCAic3ZtX3BhY2thZ2UiKQpuX3ZhbHVlcyA8LSBzZXEoMSwgMywgYnkgPSAwLjEpCk9fbiA8LSAxMF5uX3ZhbHVlcy8xMF4yCk9fbl9weXRob25fMSA8LSBPX24qcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSRUaW1lWzNdCk9fbl9SXzEgPC0gT19uKlJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSRUaW1lWzNdCk9fbl9weXRob25fMiA8LSBPX24qcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSRUaW1lWzNdCk9fbl9SXzIgPC0gT19uKlJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSRUaW1lWzNdCnBsb3RfbHkoKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IFJbUiRBbGdvcml0aG0gPT0gYWxnb3NbMV0sXSwgeCA9IH5sb2coc3FydChuKSwgMTApLCB5ID0gflRpbWUsIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ1NWTV9zY3JhdGNoIFInLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibHVlIiwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBzeW1ib2wgPSAnY2lyY2xlJykpICU+JQogIGFkZF90cmFjZShkYXRhID0gUltSJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdLCB4ID0gfmxvZyhzcXJ0KG4pLCAxMCksIHkgPSB+VGltZSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnU1ZNX2UxMDcxIFInLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibHVlIiwgZGFzaCA9ICdkYXNoZG90JyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIHN5bWJvbCA9ICdzcXVhcmUnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1sxXSxdLCB4ID0gfmxvZyhzcXJ0KG4pLCAxMCksIHkgPSB+VGltZSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnU1ZNX3NjcmF0Y2ggUHl0aG9uJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAicmVkIiwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gInJlZCIsIHN5bWJvbCA9ICdkb3QnKSkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBweXRob25bcHl0aG9uJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdLCB4ID0gfmxvZyhzcXJ0KG4pLCAxMCksIHkgPSB+VGltZSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnU1ZNX3NrbGVhcm4gUHl0aG9uJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAicmVkIiwgZGFzaCA9ICdkYXNoZG90JyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAicmVkIiwgc3ltYm9sID0gJ3NxdWFyZScpKSAlPiUKICBhZGRfdHJhY2UoeCA9IG5fdmFsdWVzLCB5ID0gT19uX1JfMSwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycsCiAgICAgICAgICAgIG5hbWUgPSAnTyhuKScsIGxpbmUgPSBsaXN0KGNvbG9yID0gJ2JsYWNrJywgZGFzaCA9ICdkYXNoJykpICU+JQogIGFkZF90cmFjZSh4ID0gbl92YWx1ZXMsIHkgPSBPX25fcHl0aG9uXzEsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLAogICAgICAgICAgICBuYW1lID0gJ08obiknLCBsaW5lID0gbGlzdChjb2xvciA9ICdibGFjaycsIGRhc2ggPSAnZGFzaCcpLCBzaG93bGVnZW5kPSBGQUxTRSkgJT4lCiAgbGF5b3V0KHRpdGxlID0gIkV4ZWN1dGlvbiBUaW1lIiwKICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gImxvZyhuKSIsIHRpY2ttb2RlID0gImFycmF5IiwgdGlja3ZhbHMgPSAwOjYsIHRpY2t0ZXh0ID0gYXMuY2hhcmFjdGVyKDA6NikpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiVGltZSAocykiKSwKICAgICAgICAgbGVnZW5kID0gbGlzdCh0aXRsZSA9ICJMZWdlbmQiLCBvcmllbnRhdGlvbiA9ICJoIiwgeCA9IDAuMywgeSA9IC0wLjIpKQpwbG90X2x5KCkgJT4lCiAgYWRkX3RyYWNlKGRhdGEgPSBSW1IkQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKHNxcnQobiksIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ1NWTV9zY3JhdGNoIFInLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibHVlIiwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBzeW1ib2wgPSAnY2lyY2xlJykpICU+JQogIGFkZF90cmFjZShkYXRhID0gUltSJEFsZ29yaXRobSA9PSBhbGdvc1syXSxdLCB4ID0gfmxvZyhzcXJ0KG4pLCAxMCksIHkgPSB+TWVtb3J5LCAKICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbmFtZSA9ICdTVk1fZTEwNzEgUicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsdWUiLCBkYXNoID0gJ2Rhc2hkb3QnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJibHVlIiwgc3ltYm9sID0gJ3NxdWFyZScpKSAlPiUKICBhZGRfdHJhY2UoZGF0YSA9IHB5dGhvbltweXRob24kQWxnb3JpdGhtID09IGFsZ29zWzFdLF0sIHggPSB+bG9nKHNxcnQobiksIDEwKSwgeSA9IH5NZW1vcnksIAogICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCBuYW1lID0gJ1NWTV9zY3JhdGNoIFB5dGhvbicsCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIsIGRhc2ggPSAnc29saWQnKSwKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJyZWQiLCBzeW1ib2wgPSAnZG90JykpICU+JQogIGFkZF90cmFjZShkYXRhID0gcHl0aG9uW3B5dGhvbiRBbGdvcml0aG0gPT0gYWxnb3NbMl0sXSwgeCA9IH5sb2coc3FydChuKSwgMTApLCB5ID0gfk1lbW9yeSwgCiAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsIG5hbWUgPSAnU1ZNX3NrbGVhcm4gUHl0aG9uJywKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAicmVkIiwgZGFzaCA9ICdkYXNoZG90JyksCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAicmVkIiwgc3ltYm9sID0gJ3NxdWFyZScpKSAlPiUKICBsYXlvdXQodGl0bGUgPSAiTWVtb3J5IHVzYWdlIiwKICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gImxvZyhuKSIsIHRpY2ttb2RlID0gImFycmF5IiwgdGlja3ZhbHMgPSAwOjYsIHRpY2t0ZXh0ID0gYXMuY2hhcmFjdGVyKDA6NikpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiTWVtb3J5IChNaUIpIiksCiAgICAgICAgIGxlZ2VuZCA9IGxpc3QodGl0bGUgPSAiTGVnZW5kIiwgb3JpZW50YXRpb24gPSAiaCIsIHggPSAwLjMsIHkgPSAtMC4yKSkKYGBgCgpCb3RoIHRoZSBSIGFuZCBQeXRob24gaW1wbGVtZW50YXRpb25zIGZyb20gc2NyYXRjaCBzZWVtIHRvIHNjYWxlIHJvdWdobHkgbGluZWFybHkgd2l0aCBzYW1wbGUgc2l6ZSBhbmQgUiBpcyBnZW5lcmFsbHkgZmFzdGVyIHRoYW4gUHl0aG9uLCBidXQgdGhlIHBhY2thZ2UgaW1wbGVtZW50YXRpb25zIG9mIFNWTSBhcmUgY29uc2lkZXJhYmx5IGZhc3Rlci4gTWVtb3J5IHVzYWdlIGlzIGFnYWluIHN0cmFuZ2VseSB2ZXJ5IGxvdyBpbiBSIGFuZCB0aGVyZSBpcyBubyBkaXNjZXJuYWJsZSB0cmVuZC4KCgojIERpc2N1c3Npb24geyNkaXNjdXNzaW9ufSAKCgojIyBFeGVjdXRpb24gdGltZQoKUiBnZW5lcmFsbHkgaGFkIGJldHRlciBleGVjdXRpb24gdGltZXMgZm9yIHNtYWxsZXIgdG8gbWVkaXVtLXNpemVkIGRhdGFzZXRzIGFjcm9zcyBhbGwgdHlwZXMgb2Ygb3BlcmF0aW9ucyBJIHRlc3RlZC4gTW9yZSBzcGVjaWZpY2FsbHksIFIgaGFkIGZhc3RlciBsb29wcyBmb3IgYWxsIHNhbXBsZSBzaXplcywgZmFzdGVyIHZlY3Rvcml6ZWQgb3BlcmF0aW9ucyBmb3Igc21hbGwtbWVkaXVtIHNhbXBsZSBzaXplcywgYW5kIGZhc3RlciBtYXRyaXggb3BlcmF0aW9ucyBmb3Igc21hbGwtbWVkaXVtIHNhbXBsZSBzaXplcy4gQ29uc2lzdGVudCB3aXRoIHRoZXNlIHJlc3VsdHMsIGxpbmVhciByZWdyZXNzaW9uIHdoaWNoIGlzIGp1c3QgaW1wbGVtZW50ZWQgYXMgbWF0cml4IG9wZXJhdGlvbnMgd2FzIGZhc3RlciBmb3Igc21hbGwtbWVkaXVtIHNhbXBsZSBzaXplcywgYW5kIHRoZSBib290c3RyYXAsIE1DTUMsIGFuZCBTVk0gd2hpY2ggYXJlIGxvb3AgYWxnb3JpdGhtcyB3ZXJlIGZhc3RlciBvdmVyYWxsIGluIFIuIFRoaXMgbWF5IGJlIGR1ZSB0byBSJ3MgZWZmaWNpZW50IGhhbmRsaW5nIG9mIHZlY3Rvcml6ZWQgb3BlcmF0aW9ucyBhbmQgaXRzIG9wdGltaXphdGlvbiBmb3Igb3BlcmF0aW9ucyBjb21tb25seSB1c2VkIGluIHN0YXRpc3RpY3MgYW5kIGRhdGEgbWFuaXB1bGF0aW9uLiAKCkhvd2V2ZXIsIFB5dGhvbiBleGhpYml0ZWQgYmV0dGVyIHBlcmZvcm1hbmNlIHdpdGggbGFyZ2UgZGF0YXNldHMsIHBhcnRpY3VsYXJseSBpbiB2ZWN0b3JpemVkIG9wZXJhdGlvbnMgYW5kIG1hdHJpeCBtYW5pcHVsYXRpb25zLiBMaWtld2lzZSwgbGluZWFyIHJlZ3Jlc3Npb24gd2FzIGZhc3RlciBpbiBQeXRob24gZm9yIGxhcmdlIHNhbXBsZSBzaXplcy4gVGhpcyBpcyBwb3NzaWJseSBkdWUgdG8gaXRzIGVmZmljaWVudCBsaWJyYXJpZXMgbGlrZSBOdW1QeSwgd2hpY2ggYXJlIGRlc2lnbmVkIHRvIGhhbmRsZSBsYXJnZSBzY2FsZSBkYXRhIGVmZmljaWVudGx5LgoKIyMgTWVtb3J5IHVzYWdlCgpJbiBtb3N0IGNhc2VzLCBJIGRvIG5vdCB0aGluayB0aGF0IG1lbW9yeSB1c2FnZSB3YXMgd2VsbC1tZWFzdXJlZCBpbiBSLCBzbyBJIHdpbGwgbm90IG1ha2Ugb3ZlcmFsbCBzdGF0ZW1lbnRzIGFib3V0IG1lbW9yeSBtYW5hZ2VtZW4uIEhvd2V2ZXIsIHRoZSBib290c3RyYXAgYW5kIE1DTUMgd2VyZSBjb3N0bHkgZm9yIG1lbW9yeSBpbiBib3RoIGxhbmd1YWdlcy4gSW4gYWRkaXRpb24sIG1lbW9yeSBtYW5hZ2VtZW50IGluIFB5dGhvbiBzZWVtZWQgdG8gYmUgZm9yIHRoZSBtb3N0IHBhcnQgYWdub3N0aWMgdG8gdGhlIHR5cGUgb2Ygb3BlcmF0aW9ucyBleGVjdXRlZCwgdXNpbmcgc2ltaWxhciBtZW1vcnkgZm9yIGEgc2ltcGxlIHN1bSBsb29wIGFuZCBmb3IgdGhlIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmUgYWxnb3JpdGhtLiAgSW4gYWRkaXRpb24sIFB5dGhvbiBkaWQgc2VlbSB0byByZXF1ZXN0IG11Y2ggbW9yZSBvZiBteSBjb21wdXRlcidzIG1lbW9yeSB0aGFuIFIsIGFuZCBJIGhhZCB0byBsZWF2ZSBpdCBydW5uaW5nIG92ZXJuaWdodCB0byBhdm9pZCBydW5uaW5nIG90aGVyIG9wZXJhdGlvbnMgc2ltdWx0YW5lb3VzbHkgb24gbXkgY29tcHV0ZXIsIGJ1dCBJIGRvIG5vdCB0aGluayB0aGlzIGlzIHdlbGwtcXVhbnRpZmllZC4gSW4gYWRkaXRpb24sIGJvdGggbGFuZ3VhZ2VzIGFyZSBnYXJiYWdlLWNvbGxlY3RlZCBsYW5ndWFnZSwgbWVhbmluZyB0aGF0IHRoZXkgYXV0b21hdGljYWxseSByZWxlYXNlIG1lbW9yeSB3aGVuIHRoZXkgY2hlY2sgdGhhdCB0aGUgb2JqZWN0cyBhcmUgbm90IHVzZWQsIGJ1dCBtZW1vcnkgYXQgYW55IGdpdmVuIHRpbWUgaXMgbm90IG5lY2Vzc2FyaWx5IHJlcHJlc2VudGF0aXZlIG9mIGNvbXB1dGF0aW9uYWwgY29tcGxleGl0eS4KCkV2ZW4gaWYgdGhlIG1lbW9yeSB1c2FnZSBvZiBhbiBvcGVyYXRpb24gd2FzIHdlbGwtbWVhc3VyZWQgaW4gUiBhbmQgUHl0aG9uLCBpdCBpcyBub3QgY2xlYXIgaWYgaXQgaXMgZGVzaXJhYmxlIHRvIGhhdmUgbG93IG9yIGhpZ2ggbWVtb3J5IHVzYWdlLCBzaW5jZSBsb3cgbWVtb3J5IHVzYWdlIGNvdWxkIGJlIGR1ZSB0byBhbiBlZmZpY2llbnQgaW1wbGVtZW50YXRpb24gb3IgcmF0aGVyIGJlIGR1ZSB0byBwb29yIG1lbW9yeSBhdHRyaWJ1dGlvbiB3aGljaCBlbmRzIHVwIHNsb3dpbmcgZG93biB0aGUgcHJvZ3JhbS4gSGVuY2UsIHRoZXJlIGlzIGEgdHJhZGVvZmYgYmV0d2VlbiBleGVjdXRpb24gdGltZSBhbmQgbWVtb3J5IHN0b3JhZ2UuCgojIyBPdGhlciBjb25zaWRlcmF0aW9ucwpJbiB0ZXJtcyBvZiBjb2RlIHdyaXRpbmcsIEkgcGVyc29uYWxseSBwcmVmZXIgUiBmb3IgbW9zdCBkYXRhIHNjaWVuY2UgcHJvamVjdHMgYmVjYXVzZSBJIGNhbiB2ZXJ5IHF1aWNrbHkgYW5hbHp5ZSBkYXRhIGluIGEgY291cGxlIGxpbmVzIHdpdGhvdXQgdGhpbmtpbmcgbXVjaCBhYm91dCB0aGUgc3ludGF4LiBBbHNvLCBJIGZpbmQgdGhlIHN5bnRheCBvZiBkcGx5ciB2ZXJ5IGNsZWFyIGZvciBkYXRhIG1hbmlwdWxhdGlvbiwgYW5kIEkgdGhpbmsgdGhhdCB0aGUgaGlnaCBxdWFsaXR5IHZpc3VhbGl6YXRpb24gb2YgZ2dwbG90MiBpcyBjbGVhcmx5IHVubWF0Y2hlZCBieSBtYXRwbG90bGliIG9yIGFueSBvdGhlciBsaWJyYXJ5IGluIHB5dGhvbi4gSG93ZXZlciwgaWYgSSBoYXZlIHRvIGltcGxlbWVudCBhIG1vcmUgY29tcGxleCBvcGVyYXRpb24sIEkgb2Z0ZW4gZmluZCBSJ3Mgc3ludGF4IGZydXN0cmF0aW5nIHNpbmNlIEkgY29uc3RhbnRseSBoYXZlIHRvIGxvb2sgdXAgb2JzY3VyZSBub3RhdGlvbnMsIGFuZCB0aGUgZXhlY3V0aW9uIGlzIGNlcnRhaW5seSBtdWNoIHNsb3dlciB0aGFuIGEgbG93LWxldmVsIGxhbmd1YWdlIGxpa2UgQy4gT24gdGhlIG90aGVyIGhhbmQsIFB5dGhvbiBpcyBtdWNoIGJldHRlci1zdXBwb3J0ZWQgZm9yIG1hY2hpbmUgbGVhcm5pbmcgYW5kIGZvciBnZW5lcmFsIHB1cnBvc2UgcHJvZ3JhbW1pbmcuCgpJbiB0aGlzIHByb2plY3QsIGEgY2hhbGxlbmdlIGZvciBtZSB3YXMgdG8gc3RvcmUgdGhlIHNpbXVsYXRlZCBtYW5uZXIgdGhhdCBjYW4gYmUgZWFzaWx5IHJlYWQgaW4gYm90aCBQeXRob24gYW5kIFIuIElmIEkgd2FzIHVzaW5nIGp1c3QgUiBJIHdvdWxkIHVzZSBhIC5yZHMgZmlsZSBjb250YWluaW5nIGFsbCB0aGUgZGF0YSBpbiBhbiBvYmplY3Qgd2l0aCBjb21wbGV4IHN0cnVjdHVyZSB0aGF0IGNhbiBiZSBlYXNpbHkgaW1wb3J0ZWQuIEhvd2V2ZXIsIHRvIGJlIHJlYWQgaW4gUHl0aG9uIEkgaGFkIHRvIHNhdmUgdGhlIGRhdGEgaW4gc2V2ZXJhbCAuY3N2IGZpbGVzIHdoaWNoIHdlcmUgcmVhZCB3aXRoIGFuIGF3a3dhcmQgc3RydWN0dXJlIGluIFB5dGhvbiBhbmQgSSBoYWQgdG8gaW5jbHVkZSBhIHdvcnJpc29tZSBlcnJvcnM9J2NvZXJjZScgbGluZSB3aGljaCBzb2x2ZWQgdGhlIHByb2JsZW0gdGhhdCBteSBmaWxlIGluY2x1ZGVkIGJvdGggc3RyaW5ncyBhbmQgbnVtZXJpY3MuIFRoaXMgaXMgbWFpbmx5IG15IGZhdWx0IGJlY2F1c2UgSSBkbyBub3Qga25vdyBob3cgdG8gcG9ncmFtIGluIFB5dGhvbiB3ZWxsLCBidXQgUiBtYWtlcyBpdCAoZGFuZ2Vyb3VzbHkpIGVhc3kgdG8gdXNlIGRpZmZlcmVudCBvYmplY3RzIHdpdGhvdXQgdGhpbmtpbmcgYWJvdXQgc3RydWN0dXJlIHRvbyBtdWNoLiBJIGZpbmQgdGhpcyB0byBiZSBhIGdyZWF0IGFkdmFudGFnZS4gRm9yIHRoZSBwdXJwb3NlIG9mIHRoaXMgcHJvamVjdCwgSSBkaWQgbm90IHJlYWxseSBjYXJlIG11Y2ggYWJvdXQgaW1wb3J0aW5nIHRoZSBkYXRhIHNtb290aGx5IHNpbmNlIEkgd2FzIGZvY3VzZWQgb24gbWFraW5nIHRoZSBhbGdvcml0aG1zIGNvbXBhcmFibGUuIEFuIGludGVyZXN0aW5nIGFzcGVjdCB0aGF0IEkgZGlkIG5vdCB0ZXN0IHdhcyB0aGUgc3BlZWQgb2YgcmVhZGluZyBkYXRhLCB3aGljaCBJIGNsZWFybHkgZG9uJ3Qga25vdyBlbm91Z2ggYWJvdXQgdG8gdGVzdCBmYWlybHksIGJ1dCBpdCBjYW4gYmUgYSBtYWpvciBjb25zaWRlcmF0aW9uIGZvciBsYXJnZSBkYXRhc2V0cy4gQW5vdGhlciB0aGluZyBJIGRpZCBub3QgY29uc2lkZXIgd2FzIHBhcmFsbGVsIGNvbXB1dGluZywgd2hpY2ggY2FuIGJlIGltcGxlbWVudGVkIGluIGJvdGggUiBhbmQgUHl0aG9uIGFuZCBjYW4gc2lnbmlmaWNhbnRseSBzcGVlZCB1cCBjb21wdXRhdGlvbnMuIFRoZXJlIGFyZSBhbHNvIG1hbnkgb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzIHRoYW4gUHl0aG9uIGFuZCBSIHRoYXQgY2FuIGJlIHVzZWQgaW4gZGF0YSBzY2llbmNlLCBzdWNoIGFzIEp1bGlhIG9yIG9uIHRoZSBvdGhlciBlbmQgb2YgYWJzdHJhY3Rpb24sIEMvQysrLgoKIyMgSW50ZXJhY3RpdmUgdmlzdWFsaXphdGlvbgpJIHVzZWQgcGxvdGx5IHRvIG1ha2UgdGhlIGludGVyYWN0aXZlIHBsb3RzIGluIHRoaXMgcGFnZSwgYW5kIGl0IGlzIHRoZSBmaXJzdCB0aW1lIEkgdXNlZCBpbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9uLiBJIGRpZCBub3QgcmVhbGx5IGxpa2UgdGhpcyBleHBlcmllbmNlIGJlY2F1c2UgSSB0aGluayB0aGUgcGxvdHMgbW9zdGx5IGVuZCB1cCBkaXN0cmFjdGluZyB0aGUgdXNlciB3aXRob3V0IGNvbnZleWluZyBtdWNoIG1vcmUgaW5mb3JtYXRpb24uIEkgZmluZCBleHBsb3JhdG9yeSBkYXRhIHZpc3VhbGl6YXRpb24gbXVjaCBlYXNpZXIgYW5kIGJlYXV0aWZ1bCBpbiBnZ3Bsb3QyIG9yIGV2ZW4gYmFzZSBSLiBIb3dldmVyLCBpdCBhbHNvIHNlZW1zIG1vcmUgaW50ZXJlc3RpbmcgYW5kIGV5ZS1jYXRjaGluZyB0aGFuIGEgYmxhY2stYW5kLXdoaXRlIHBsb3QgeW91IHdvdWxkIHJlYWQgaW4gYSBwYXBlciwgcGFydGljdWxhcmx5IGZvciBibG9nIHBvc3QgZm9ybWF0IGxpa2UgdGhpcy4KCkkgYWxzbyBjaG9zZSB0byBwbG90IHRoZSBkYXRhIG9uIGEgbG9nIHNjYWxlLCB3aGljaCBpcyBkaWZmaWN1bHQgdG8gaW50ZXJwcmV0LiBGb3IgaW5zdGFuY2UsIHdlIHNlZSB0aGF0IGxpbmVhciBmdW5jdGlvbnMgYmVjb21lIHZlcnkgZGlzdG9ydGVkLiBIb3dldmVyLCBJIHRob3VnaHQgdGhpcyBjaG9pY2Ugd2FzIHJlYXNvbmFibGUgdG8gaGlnaGxpZ2h0IHRoZSBkaWZmZXJlbnQgbmVlZHMgb2YgZGF0YSBzY2llbmNlIGZyb20gc21hbGwgc2FtcGxlcywgd2hpY2ggaXMgbXkgbWFpbiBhcmVhIG9mIGludGVyZXN0LCB0byBiaWcgZGF0YS4gQXMgYSBzdGF0aXN0aWNpYW4sIEkgYWxzbyB3b3VsZCBoYXZlIHdpc2hlZCB0byBjb252ZXkgdW5jZXJ0YWludHkgaW4gdGhlc2UgZXN0aW1hdGVzLiBJIHVzZWQgMTAgaXRlcmF0aW9ucyBvZiBlYWNoIGFsZ29yaXRobSBhbmQgY29tcHV0ZWQgdGhlIG1lZGlhbiwgYnV0IGl0IG1heSBoYXZlIGJlZW4gaW50ZXJlc3RpbmcgdG8gZmluZCBhIHdheSB0byBpbmNsdWRlIHVuY2VydGFpbnR5IG9uIHRoZSBwbG90cywgYWx0aG91Z2ggaXQgd291bGQgaGF2ZSBtYWRlIHRoZSBwbG90cyBtb3JlIGNsdXR0ZXJlZCB0aGFuIHRoZXkgYWxyZWFkeSBhcmUuCgpUaGlzIHdhcyBhbHNvIG15IGZpcnN0IHRpbWUgY3JlYXRpbmcgYSB3ZWJzaXRlLCB3aGljaCBJIGZvcmtlZCBmcm9tIG1taXN0YWtlcy9taW5pbWFsLW1pc3Rha2VzLiBJIGxpa2VkIHRoaXMgZm9ybWF0IG9mIHVzaW5nIG1hcmtkb3duIG5vdGVib29rLCB3aGljaCBhbGxvd2VkIHRvIGVhc2lseSBpbmNsdWRlIHRleHQsIGNvZGUsIGFuZCBwbG90cyBmcm9tIGRpZmZlcmVudCBwcm9ncmFtbWluZyBsYW5ndWFnZXMgaW4gYW4gYWVzdGhldGljYWxseSBwbGVhc2luZyBmb3JtYXQuIEhvd2V2ZXIsIHRoZSBlYXNpbmVzcyBvZiB3cml0aW5nIGluIGEgbWFya2Rvd24gZmlsZSBhcyBvcHBvc2VkIHRvIEhUTUwgYW5kIENTUyBmaWxlcyBjb21lcyBhdCB0aGUgY29zdCBvZiBjdXN0b21pemF0aW9uLgoKIyMgSXMgUiBvciBQeXRob24gYmV0dGVyPwpDbGVhcmx5LCBjb21wdXRhdGlvbmFsIHBlcmZvcm1hbmNlIGlzIGhpZ2hseSBkZXBlbmRlbnQgb24gdGhlIHNpdHVhdGlvbiwgaW5jbHVkaW5nIHRoZSBkYXRhIGRpbWVuc2lvbiwgdGhlIGFsZ29yaXRobSBvZiBpbnRlcmVzdCwgdGhlIGVhc2luZXNzIG9mIG9wdGltaXppbmcgY29kZSwgYW5kIHRoZSBtYWNoaW5lIHVzZWQgdG8gY29tcHV0ZSBpdC4gVGhlcmVmb3JlLCBJIGNhbm5vdCBldmVuIGFuc3dlciB0aGUgcXVlc3Rpb24gb2Ygd2hldGhlciBSIG9yIFB5dGhvbiBpcyBmYXN0ZXIgZm9yIG15IHVzZXMgYW5kIG15IGNvbXB1dGVyLCBsZXQgYWxvbmUgZm9yIGFueW9uZSBlbHNlLiBIb3dldmVyLCBpbiBtb3N0IG9mIG15IGV2ZXJ5ZGF5IGRhdGEgc2NpZW5jZSBvcGVyYXRpb25zLCBteSBzYW1wbGUgc2l6ZXMgYXJlIGJlbG93ICQxMF41JCwgYW5kIHRoaXMgc2ltdWxhdGlvbiBzaG93cyBtZSB0aGF0IGluIHRoYXQgY29udGV4dCBSIGtlZXBzIHVwIHdpdGggUHl0aG9uIGFuZCwgdG8gbXkgc3VycHJpc2UsIGlzIG9mdGVuIGJldHRlci4gSW4gc29tZSBjYXNlcywgc3VjaCBhcyB2ZXJ5IGxhcmdlIHNhbXBsZSBzaXplcyBvciBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMgd2l0aCB3ZWxsLWltcGxlbWVudGVkIGxpYnJhcmllcywgUHl0aG9uIG1heSBiZSBzaWduaWZpY2FudGx5IGZhc3Rlci4gT3ZlcmFsbCwgaXQgZG9lcyBzZWVtIHRoYXQgUiB3YXMgbXVjaCBmYXN0ZXIgZm9yIG1lLCB3aGljaCBpcyBwYXJ0aWN1bGFybHkgY2xlYXIgdGhyb3VnaCB0aGUgZmFjdCB0aGF0IHRoZSBlbnRpcmUgc2NyaXB0IHdhcyBydW4gaW4gOSBtaW51dGVzIGluIFIgYW5kIGluIDEyNiBtaW51dGVzIGluIFB5dGhvbi4K